Unity 新手引导 遮罩效果

效果如图所示:固定大小模式,边缘大小可以调节

效果如图所示:动画缩放模式,边缘大小可以调节

结构如图所示:

资源下载

  1. 在UGUI中创建一个Image,颜色设置为黑色,调整透明度

  2. 在Image上添加脚本<Test> <GuideController> <RectGuide> <CircleGuide>

Test:测试脚本,用于调用类

using UnityEngine;

public class Test : MonoBehaviour
{
    GuideController guideController;
    Canvas canvas;
    public GameObject game;

    private void Start()
    {
        canvas = transform.GetComponentInParent<Canvas>();
        guideController = transform.GetComponent<GuideController>();

        guideController.Guide(canvas, game.GetComponent<RectTransform>(), GuideType.Circle, 10, 1f);
//画布,物体,形状(圆形、矩形),起势边距,时间
    }

    private void Update()
    {
        //guideController.Guide(canvas, game.GetComponent<RectTransform>(), GuideType.Circle);
//画布,物体,形状(圆形、矩形)

    }

}

GuideController:总控制脚本

using UnityEngine;
using UnityEngine.UI;

public enum GuideType
{
    Rect,//矩形
    Circle,//圆形
}

[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
    private CircleGuide circleGuide;
    private RectGuide rectGuide;

    public Material rectMat;
    public Material circleMat;
    private Image mask;
    private RectTransform target;
    private void Awake()
    {
        mask = transform.GetComponent<Image>();
        if (mask == null) { throw new System.Exception("mask初始化失败"); }
        if (rectMat == null || circleMat == null) { throw new System.Exception("材质未赋值"); }
        rectGuide = transform.GetComponent<RectGuide>();
        circleGuide = transform.GetComponent<CircleGuide>();
    }

    public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
    {
        this.target = target;
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target);
                break;

        }
    }

    public void Guide(Canvas canvas, RectTransform target, GuideType guideType,float scale,float time)
    {
        this.target = target;
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target, scale,time);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target, scale, time);
                break;

        }
    }

    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        if (target==null) { return true; }
        return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
    }
}

RectGuide:矩形控制脚本

using UnityEngine;

public class RectGuide : GuideBase
{
    protected float width;
    protected float height;

    float scalewidth;
    float scaleheight;

    public float widths, heights;//设置宽高 余量
    public override void Guide(Canvas canvas, RectTransform target)
    {
        base.Guide(canvas, target);
        //计算宽高
        width = (targetCorners[3].x - targetCorners[0].x) / 2;
        height = (targetCorners[1].y - targetCorners[0].y) / 2;
        material.SetFloat("_SliderX", width + widths);
        material.SetFloat("_SliderY", height + heights);
    }

    public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
    {
        this.Guide(canvas, target);

        scalewidth = width * scale;
        scaleheight = height * scale;
        material.SetFloat("_SliderX", scalewidth);
        material.SetFloat("_SliderY", scaleheight);

        this.time = time;
        isScaling = true;
        timer = 0;

    }

    protected override void Update()
    {
        base.Update();
        if (isScaling)
        {
            this.material.SetFloat("_SliderX", Mathf.Lerp(scalewidth, width, timer));
            this.material.SetFloat("_SliderY", Mathf.Lerp(scaleheight, height, timer));
        }
    }
}

CircleGuide:圆形控制脚本

using UnityEngine;

public class CircleGuide : GuideBase
{
    private float r;//镂空半径
    private float scaleR;//变化之后的半径大小

    public float radius;//半径 余量


    public override void Guide(Canvas canvas, RectTransform target)
    {
        base.Guide(canvas, target);
        //计算半径
        float width = (targetCorners[3].x - targetCorners[0].x) / 2;
        float height = (targetCorners[1].y - targetCorners[0].y) / 2;
        r = Mathf.Sqrt(width * width + height * height);
        this.material.SetFloat("_Slider", r + radius);
    }

    public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
    {
        this.Guide(canvas, target);
        scaleR = r * scale;
        this.material.SetFloat("_Slider", scaleR);

        this.time = time;
        isScaling = true;
        timer = 0;
    }

    protected override void Update()
    {
        base.Update();
        if (isScaling)
        {
            this.material.SetFloat("_Slider", Mathf.Lerp(scaleR, r, timer));
        }
    }

}
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Image))]
public class GuideBase : MonoBehaviour
{
    protected Material material;//材质
    protected Vector3 center;//镂空中心
    protected RectTransform target;
    protected Vector3[] targetCorners = new Vector3[4];//引导目标的边界

    protected float timer;
    protected float time;
    protected bool isScaling;

    protected virtual void Update()
    {
        if (isScaling)
        {
            timer += Time.deltaTime * 1 / time;
            if (timer >= 1)
            {
                timer = 0;
                isScaling = false;
            }
        }
    }

    public virtual void Guide(Canvas canvas, RectTransform target)
    {
        material = GetComponent<Image>().material;
        this.target = target;
        //获取四个点的世界坐标
        target.GetWorldCorners(targetCorners);
        //世界坐标转屏幕坐标
        for (int i = 0; i < targetCorners.Length; i++)
        {
            targetCorners[i] = WorldToScreenPoints(canvas, targetCorners[i]);
        }
        //计算中心点
        center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
        center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
        //设置中心点
        material.SetVector("_Center", center);
    }

    public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
    {

    }

    public Vector2 WorldToScreenPoints(Canvas canvas, Vector3 world)
    {
        //把世界转屏幕
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
        Vector2 localPoint;
        //屏幕转局部坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), screenPoint, canvas.worldCamera, out localPoint);
        return localPoint;
    }
}

shader脚本:

Shader "UI/CircleGuide"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)

        _StencilComp("Stencil Comparison", Float) = 8
        _Stencil("Stencil ID", Float) = 0
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255

        _ColorMask("Color Mask", Float) = 15

       // [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
        _Center("Center",vector) = (0,0,0,0)
        _Slider("Slider",Range(0,2500)) = 2500
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType" = "Plane"
                "CanUseSpriteAtlas" = "True"
            }

            Stencil
            {
                Ref[_Stencil]
                Comp[_StencilComp]
                Pass[_StencilOp]
                ReadMask[_StencilReadMask]
                WriteMask[_StencilWriteMask]
            }

            Cull Off
            Lighting Off
            ZWrite Off
            ZTest[unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask[_ColorMask]

            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0

                #include "UnityCG.cginc"
                #include "UnityUI.cginc"

                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP

                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                sampler2D _MainTex;
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float4 _MainTex_ST;
                float2 _Center;
                float _Slider;

                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                    OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                    OUT.color = v.color * _Color;
                    return OUT;
                }

                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif

                    #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001);
                    #endif
                    color.a *= (distance(IN.worldPosition.xy,_Center.xy) > _Slider);
                    color.rgb *= color.a;

                    return color;
                }
            ENDCG
            }
        }
}
Shader "UI/RectGuide"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)

        _StencilComp("Stencil Comparison", Float) = 8
        _Stencil("Stencil ID", Float) = 0
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255

        _ColorMask("Color Mask", Float) = 15

        //[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0

        _Center("Center",vector) = (0,0,0,0)
        _SliderX("SliderX",Range(0,1500)) = 1500
        _SliderY("SliderY",Range(0,1500)) = 1500
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType" = "Plane"
                "CanUseSpriteAtlas" = "True"
            }

            Stencil
            {
                Ref[_Stencil]
                Comp[_StencilComp]
                Pass[_StencilOp]
                ReadMask[_StencilReadMask]
                WriteMask[_StencilWriteMask]
            }

            Cull Off
            Lighting Off
            ZWrite Off
            ZTest[unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask[_ColorMask]

            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0

                #include "UnityCG.cginc"
                #include "UnityUI.cginc"

                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP

                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                sampler2D _MainTex;
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float4 _MainTex_ST;

                float2 _Center;
                float _SliderX;
                float _SliderY;

                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                    OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                    OUT.color = v.color * _Color;
                    return OUT;
                }

                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif

                    #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001);
                    #endif
                    float2 dis = IN.worldPosition.xy - _Center.xy;
                    color.a *= (abs(dis.x) > _SliderX) || (abs(dis.y) > _SliderY);
                    color.rgb *= color.a;

                    return color;
                }
            ENDCG
            }
        }
}

资源下载

如有疑问 可以再评论区留言

猜你喜欢

转载自blog.csdn.net/li1214661543/article/details/112789372