新手引导遮罩


Shader "Hidden/GuideMask"
{
    Properties
    {
        _Rect("Rect",vector)=(0,0,0,0)
        _BackgroundColor("_BackgroundColor",Color)=(0,0,0,0.3)
        _FrontColor("_FrontColor",Color)=(0,0,0,0.2)
        _Radian("_Radian",Range(0,1))=0.5
    }
    SubShader
    {
        Cull Off ZWrite Off ZTest Always
        Pass
        {
            Tags{"Queue"="Transparent"}
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            float4 _Rect;
            fixed4 _BackgroundColor;
            fixed4 _FrontColor;
            float _Radian;
            fixed InCircle(float2 center,float radius,float2 uv)
            {
                return step(radius,distance(center,uv));
            }

            float Fit(float2 pos)
            {
                pos=pos*_ScreenParams.xy;
                float2 halfSize= _Rect.zw/2;
                float halfWidth=halfSize.x;
                float halfHeight=halfSize.y;
                float _min= min(halfWidth,halfHeight);
                float radius = _min*_Radian;

                float2 horizontalsize=halfSize-float2(radius,0);
                float2 verticalsize=halfSize-float2(0,radius);
                fixed2 h=step(_Rect.xy-horizontalsize,pos)*step(pos,_Rect.xy+horizontalsize);
                fixed2 v=step(_Rect.xy-verticalsize,pos)*step(pos,_Rect.xy+verticalsize);
                float _r=max(h.x*h.y,v.x*v.y);

                float2 center=_Rect.xy;
                float witdhBigger=step(halfHeight,halfWidth);
                float heightBigger=1-witdhBigger;
                float _add=_min*(1-_Radian);
                float gap= max(halfWidth,halfHeight)-_min;

                float2 lu=center+float2(-gap*witdhBigger,gap*heightBigger)+float2(-_add,_add);
                float2 ld=center+float2(-gap*witdhBigger,-gap*heightBigger)+float2(-_add,-_add);
                float2 ru=center+float2(gap*witdhBigger,gap*heightBigger)+float2(_add,_add);
                float2 rd=center+float2(gap*witdhBigger,-gap*heightBigger)+float2(_add,-_add);

                float _c= InCircle(lu,radius,pos)*InCircle(ru,radius,pos)*InCircle(ld,radius,pos)*InCircle(rd,radius,pos);
                return max(_r,1-_c);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return lerp(_BackgroundColor,_FrontColor,Fit(i.uv));
            }
            ENDCG
        }
    }
}
    public class GuideMask : Graphic, ICanvasRaycastFilter
    {
        [SerializeField] private Vector2 _center;
        [SerializeField] private Vector2 _size;
        [SerializeField] private Vector2 _margin=Vector2.one*10;

        [SerializeField] private float _radian = 0.2f;
        [SerializeField] private bool _raycastInRect;
        [SerializeField] private bool _raycastPass;

        public Vector2 center
        {
            get { return _center; }
        }
        public Vector2 size
        {
            get { return _size; }
        }
        public Rect rect { get { return new Rect() { size = size, center = center, }; } }
        public bool raycastInRect { get { return _raycastInRect; } set { _raycastInRect = value; } }
        public bool raycastPass { get { return _raycastPass; } set { _raycastPass = value; } }
        public Vector2 margin { get { return _margin; } set { _margin = value; } }

        public float radian { get { return _radian; } }
        public Color background
        {
            get
            {
                if (material == null) return Color.white;
                return material.GetColor("_BackgroundColor");
            }
            set
            {
                if (material == null) return;
                material.SetColor("_BackgroundColor", value);
            }
        }
        public override Color color
        {
            get
            {
                if (material == null) return Color.white;
                return material.GetColor("_FrontColor");
            }
            set
            {
                if (material == null) return;
                material.SetColor("_FrontColor", value);
            }
        }

        public void SetRect(Rect rect)
        {
            this._center = rect.center;
            this._size = rect.size;
            if (material == null) return;
            material.SetVector("_Rect", new Vector4(center.x, center.y, size.x, size.y));
        }
        public void SetRadian(float radian)
        {
            this._radian = radian;
            if (material == null) return;

            material.SetFloat("_Radian", radian);
        }

        public Rect GetFocusRect(RectTransform trans,Vector2 offset)
        {
            Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(trans);
            Vector2 size = bounds.extents * 2;
            Canvas canvas = trans.GetComponentInParent<Canvas>();
            Vector2 center = Vector2.zero;
            switch (canvas.renderMode)
            {
                case RenderMode.ScreenSpaceOverlay:
                    center = RectTransformUtility.WorldToScreenPoint(null, trans.position);
                    break;
                case RenderMode.ScreenSpaceCamera:
                case RenderMode.WorldSpace:
                    center = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, trans.position);
                    break;
                default:
                    break;
            }
            return new Rect(center - (Vector2)bounds.extents + offset - margin/2, size+ margin);
        }
        public Rect GetFocusRect(Transform trans, Vector2 size, Camera camera)
        {
            if (camera == null)
                camera = Camera.main;
            Vector2 center = camera.WorldToScreenPoint(trans.position);
            return new Rect() { size = size +margin, center = center - margin / 2, };
        }

        public void Focus(RectTransform trans, Vector2 offset)
        {
            SetRect(GetFocusRect(trans,offset));
        }
        public void Focus(Transform trans, Vector2 size, Camera camera)
        {
            SetRect(GetFocusRect(trans, size, camera));
        }
        private bool Contains(Rect rect, Vector2 point)
        {
            return rect.Contains(point);
        }


        protected override void Awake()
        {
            SetRect(this.rect);
            SetRadian(this._radian);
        }

        public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
        {
            if (!raycastPass) return true;
            if (raycastInRect) return !Contains(rect, sp);
            return false;
        }
    }
    [CustomEditor(typeof(GuideMask))]
    class GuideMaskEditor : Editor
    {
        GuideMask gm { get { return target as GuideMask; } }
        public override void OnInspectorGUI()
        {
            EditorGUI.BeginChangeCheck();
            gm.material= EditorGUILayout.ObjectField("Material", gm.material, typeof(Material), false) as Material;
            gm.color = EditorGUILayout.ColorField("Color", gm.color);
            gm.background = EditorGUILayout.ColorField("Background Color", gm.background);
            gm.raycastTarget = EditorGUILayout.Toggle("Raycast Target", gm.raycastTarget);
            gm.raycastInRect = EditorGUILayout.Toggle("Raycast In Rect", gm.raycastInRect);
            gm.raycastPass = EditorGUILayout.Toggle("Raycast Pass", gm.raycastPass);

            var center = EditorGUILayout.Vector2Field("Center", gm.center);
            var size = EditorGUILayout.Vector2Field("Size", gm.size);
            gm.margin = EditorGUILayout.Vector2Field("Margin", gm.margin);
            var radian= EditorGUILayout.Slider("Radian", gm.radian, 0, 1);


            if (EditorGUI.EndChangeCheck())
            {
                gm.SetRadian(radian);
                gm.SetRect(new Rect() { size = size, center = center, });
               
                serializedObject.ApplyModifiedProperties();
            }
        }
    }