unity实现擦玻璃效果

初始
初始时的样子
之后效果
拖动擦除,露出背景的白色

准备
这里用了一个父物体,下面设置两张图,一张Image作为背景,也就是需要擦除显露出来的图片,另一张RawImage作为遮罩图,也就是需要擦除的图片。

画布设置
画布这里需要设置成相机

在RawImage上挂载处理脚本

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 因为用的是localpos 所以遮罩和检测的单位需要注意在同一个坐标系下
/// </summary>
public class UIEraseMask : MonoBehaviour
{
    
    
    private RectTransform m_rectTransform;
    private RawImage m_imgMask;
    private Texture2D m_imgTexture;
    private int m_imgWidth;
    private int m_imgHeight;

    private int m_brushSize;
    private Camera m_uiCamera;

    //属性
    bool startDraw = false;
    bool twoPoints = false;
    Vector2 lastPos; //最后一个点
    Vector2 penultPos; //倒数第二个点
    float radius = 12f;
    float distance = 1f;
    private Vector3 m_deviant;

    public void InitMaskParam(RectTransform parentRect, Camera c, int imgWidth, int imgHight, Vector2 deviant, int brushSize = 20)
    {
    
    
        m_uiCamera = c;
        m_deviant = deviant;
        m_imgMask = GetComponent<RawImage>();
        m_rectTransform = parentRect;
        m_imgTexture = new Texture2D(imgWidth, imgHight);
        m_imgWidth = imgWidth;
        m_imgHeight = imgHight;
        m_brushSize = brushSize;
        m_imgTexture.Apply();
        m_imgMask.texture = m_imgTexture;

        InitEvent();
    }

    private void InitEvent()
    {
    
    
        var trigger = gameObject.AddComponent<EventTrigger>();
        EventTrigger.Entry entry = new EventTrigger.Entry();
        entry.eventID = EventTriggerType.PointerDown;
        entry.callback = new EventTrigger.TriggerEvent();
        entry.callback.AddListener((data) =>
        {
    
    
            var pointerEventData = (PointerEventData)data;
            OnPointerDown(pointerEventData);
        });
        trigger.triggers.Add(entry);
        entry = new EventTrigger.Entry();
        entry.eventID = EventTriggerType.PointerUp;
        entry.callback = new EventTrigger.TriggerEvent();
        entry.callback.AddListener((data) =>
        {
    
    
            var pointerEventData = (PointerEventData)data;
            OnPointerUp(pointerEventData);
        });
        trigger.triggers.Add(entry);
        entry = new EventTrigger.Entry();
        entry.eventID = EventTriggerType.Drag;
        entry.callback = new EventTrigger.TriggerEvent();
        entry.callback.AddListener((data) =>
        {
    
    
            var pointerEventData = (PointerEventData)data;
            OnDrag(pointerEventData);
        });
        trigger.triggers.Add(entry);
    }



    /// <summary>
    /// 贝塞尔平滑
    /// </summary>
    /// <param name="start">起点</param>
    /// <param name="mid">中点</param>
    /// <param name="end">终点</param>
    /// <param name="segments">段数</param>
    /// <returns></returns>
    public Vector2[] Beizier(Vector2 start, Vector2 mid, Vector2 end, int segments)
    {
    
    
        float d = 1f / segments;
        Vector2[] points = new Vector2[segments - 1];
        for (int i = 0; i < points.Length; i++)
        {
    
    
            float t = d * (i + 1);
            points[i] = (1 - t) * (1 - t) * mid + 2 * t * (1 - t) * start + t * t * end;
        }

        List<Vector2> rps = new List<Vector2>();
        rps.Add(mid);
        rps.AddRange(points);
        rps.Add(end);
        return rps.ToArray();
    }


    #region 事件

    public void OnPointerDown(PointerEventData eventData)
    {
    
    
        startDraw = true;
        penultPos = eventData.position;
        CheckPoint(penultPos);
    }

    public void OnDrag(PointerEventData eventData)
    {
    
    
        if (twoPoints && Vector2.Distance(eventData.position, lastPos) > distance) //如果两次记录的鼠标坐标距离大于一定的距离,开始记录鼠标的点
        {
    
    
            Vector2 pos = eventData.position;
            float dis = Vector2.Distance(lastPos, pos);

            CheckPoint(eventData.position);
            int segments = (int)(dis / radius); //计算出平滑的段数
            segments = segments < 1 ? 1 : segments;
            if (segments >= 10)
            {
    
    
                segments = 10;
            }

            Vector2[] points = Beizier(penultPos, lastPos, pos, segments); //进行贝塞尔平滑
            for (int i = 0; i < points.Length; i++)
            {
    
    
                CheckPoint(points[i]);
            }

            lastPos = pos;
            if (points.Length > 2)
                penultPos = points[points.Length - 2];
        }
        else
        {
    
    
            twoPoints = true;
            lastPos = eventData.position;
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
    
    
        startDraw = false;
        twoPoints = false;
    }


    #endregion

    void CheckPoint(Vector3 pScreenPos)
    {
    
    
        Vector3 localPos = ScreenPointToLocalPointInRectangle(m_rectTransform, m_uiCamera, pScreenPos);

        localPos -= m_deviant;

        if (localPos.x > -m_imgWidth / 2 && localPos.x < m_imgWidth / 2 && localPos.y > -m_imgHeight / 2 &&
            localPos.y < m_imgHeight / 2)
        {
    
    
            for (int i = (int)localPos.x - m_brushSize; i < (int)localPos.x + m_brushSize; i++)
            {
    
    
                for (int j = (int)localPos.y - m_brushSize; j < (int)localPos.y + m_brushSize; j++)
                {
    
    
                    if (Mathf.Pow(i - localPos.x, 2) + Mathf.Pow(j - localPos.y, 2) > Mathf.Pow(m_brushSize, 2))
                        continue;
                    if (i < 0)
                    {
    
    
                        if (i < -m_imgWidth / 2)
                        {
    
    
                            continue;
                        }
                    }

                    if (i > 0)
                    {
    
    
                        if (i > m_imgWidth / 2)
                        {
    
    
                            continue;
                        }
                    }

                    if (j < 0)
                    {
    
    
                        if (j < -m_imgHeight / 2)
                        {
    
    
                            continue;
                        }
                    }

                    if (j > 0)
                    {
    
    
                        if (j > m_imgHeight / 2)
                        {
    
    
                            continue;
                        }
                    }

                    Color col = m_imgTexture.GetPixel(i + (int)m_imgWidth / 2, j + (int)m_imgHeight / 2);
                    if (col.a != 0f)
                    {
    
    
                        col.a = 0.0f;
                        m_imgTexture.SetPixel(i + (int)m_imgWidth / 2, j + (int)m_imgHeight / 2, col);
                    }
                }
            }

            m_imgTexture.Apply();
        }
    }

    public static Vector2 ScreenPointToLocalPointInRectangle(RectTransform parentRect, Camera c, Vector3 screenPos)
    {
    
    
        Vector2 vector2 = Vector2.zero;
        Vector2 vec2Pos = vector2;
        vec2Pos.x = screenPos.x;
        vec2Pos.y = screenPos.y;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, vec2Pos,
            c, out vector2);
        return vector2;
    }
}

写一个初始化的脚本

using UnityEngine;
using UnityEngine.UI;

public class Mgr : MonoBehaviour
{
    
    
    public RawImage m_rimgMask;

    public RectTransform m_rect;
    public Camera m_camera;
    public Transform m_parentTrans;

    private void Start()
    {
    
    
        m_rimgMask.GetComponent<UIEraseMask>().InitMaskParam(m_rect, m_camera, (int)m_rimgMask.rectTransform.rect.width,
                (int)m_rimgMask.rectTransform.rect.height, m_parentTrans.localPosition);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_47819574/article/details/131294142