Unity UI擦除效果

发布于:2024-04-24 ⋅ 阅读:(28) ⋅ 点赞:(0)
public class ScratchImage : MonoBehaviour
    {
        /// <summary>
        /// 蒙版贴图
        /// </summary>
        public Image maskImage;
        public Material maskMaterial;
        private Camera uiCamera;
        private Vector2 _maskSize;
        private Texture2D _rt;
        private Color[] spritePixels;
        public int brushRadius = 50;
        private float percent = 0;
        public float finishPercent = 0.95f;
        private int clearNum = 0;
        private int[] dirtyPoints;
        private Vector2 SCALE_FACTOR;
        private Vector2Int RTSize;
        bool bClear = false;
        private void Reset()
        {
            maskImage = GetComponent<Image>();
            maskMaterial = maskImage.material;
        }
        public void SetParams(int brushRadius,float fpercent)
        {
            this.brushRadius = brushRadius;
            this.finishPercent = fpercent;
        }
        private void Init()
        {
            if (uiCamera == null)
            {
                uiCamera = UIHelper.Instance.UICamera;
            }
            SCALE_FACTOR = maskImage.sprite.textureRect.size / maskImage.rectTransform.rect.size;
            _maskSize = maskImage.rectTransform.rect.size;
            //Debug.LogFormat("mask image size:{0}*{1}", maskSize.x, maskSize.y);
            RTSize = new Vector2Int((int)maskImage.sprite.textureRect.size.x, (int)maskImage.sprite.textureRect.size.y);
            spritePixels = maskImage.sprite.texture.GetPixels(0, 0, RTSize.x, RTSize.y);
            _rt = new Texture2D(RTSize.x, RTSize.y);
            _rt.SetPixels(spritePixels);
            _rt.Apply();
            maskMaterial.SetTexture("_TempTex", _rt);
            dirtyPoints = new int[RTSize.x * RTSize.y];
            bClear = false;
            percent = 0;
            clearNum = 0;
        }
        public void Update()
        {
            onUpdate();
        }
        void Fill()
        {
            bClear = true;
            for (int i = 0; i < RTSize.x; i++)
            {
                for (int j = 0; j < RTSize.y; j++)
                {
                    if(dirtyPoints[i*RTSize.y+j]==0)
                    {
                        _rt.SetPixel(i, j, new Color(1, 1, 1, 0));
                    }
                }
            }
            _rt.Apply();
        }
        public void ResetMask()
        {
            _rt.SetPixels(spritePixels);
            _rt.Apply();
            for (int i = 0; i < dirtyPoints.Length; i++)
            {
                dirtyPoints[i] = 0;
            }
            clearNum = 0;
            bClear = false;
        }
        private void onUpdate()
        {
            if (bClear) return;
            if (uiCamera == null)
                return;
            if (!Input.GetMouseButton(0)) // 移动鼠标或者处于按下状态
            {
                return;
            }
            Vector2 localPt = Vector2.zero;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, Input.mousePosition, uiCamera, out localPt);

            //Debug.Log($"pt:{localPt}, status:{mouseStatus}");
            if (localPt.x < 0 || localPt.y < 0 || localPt.y >= _maskSize.x || localPt.y >= _maskSize.y)
                return;

            localPt = localPt * SCALE_FACTOR;
            Vector2Int usePixel = new Vector2Int((int)(localPt.x), (int)localPt.y);
            int left = (int)(usePixel.x - brushRadius);
            if (left < 0) left = 0;
            int right = (int)(usePixel.x + brushRadius);
            if (right > RTSize.x) right = (int)RTSize.x;
            int down = (int)(usePixel.y - brushRadius);
            if (down < 0) down = 0;
            int top = (int)(usePixel.y + brushRadius);
            if (top > RTSize.y) top = (int)RTSize.y;
            bool dirty = false;
            for (int i = left; i < right; i++)
            {
                for (int j = down; j < top; j++)
                {
                    int index = (int)(i * RTSize.y + j);
                    if (dirtyPoints[index] == 1)
                    {

                    }
                    else
                    {
                        Vector2 p = new Vector2(i, j);
                        float dis = Vector2.Distance(usePixel, p);
                        if (dis <= brushRadius)
                        {
                            _rt.SetPixel(i, j, new Color(1, 1, 1, 0));
                            dirtyPoints[index] = 1;
                            ++clearNum;
                            dirty = true;
                        }
                    }
                }
            }
            if (dirty)
            {
                percent = (float)clearNum / dirtyPoints.Length;
                if (percent > finishPercent)
                {
                    Fill();
                    GameHelp.FireEvent((ushort)DGlobalEvent.EVENT_SMALL_GAME_RESULT_OK, 0, 0, null);
                }
                else
                {
                    _rt.Apply();
                }
            }
        }
    }

shader

Shader "Unlit/PaintMask"
{
    Properties
    {
        _MainTex ("MainTexture", 2D) = "white" {}
        _TempTex ("TempTexture",2D) = "white" {}
    }
    SubShader
    {
        Tags 
        { 
           "Queue"="Transparent"
           "RenderType"="Transparent"
        }
        LOD 100
        ZWrite Off
        ZTest Off
        Blend SrcAlpha OneMinusSrcAlpha 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _TempTex;
            float4 _TempTex_ST;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                half alpha = tex2D(_TempTex,i.uv).a;
                col.a = alpha;
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

最粗暴的方法,原理就是跟着鼠标的位置,以鼠标为圆心把圆内的像素点清除,遮罩变成透明了,里面的图片就显示出来了


网站公告

今日签到

点亮在社区的每一天
去签到