UGUI实现擦除效果(不用shader)

using UnityEngine;
using UnityEngine.UI;

public class ScratchCard : MonoBehaviour
{
    /// <summary>
    /// 笔刷大小
    /// </summary>
    public float brushSize = 50f;

    /// <summary>
    /// 遮罩图片
    /// </summary>
    public Image maskImage;

    /// <summary>
    /// 摄像机
    /// </summary>
    public Camera Camera;

    /// <summary>
    /// 要操作的纹理
    /// </summary>
    private Texture2D scratchableTexture;
    
    /// <summary>
    /// 区域
    /// </summary>
    private RectTransform rectTransform;

    void Start()
    {
        //新建一个纹理 并填充纹理为遮罩纹理
        scratchableTexture = new Texture2D(maskImage.sprite.texture.width, maskImage.sprite.texture.height, TextureFormat.RGBA32, false);
        Graphics.CopyTexture(maskImage.sprite.texture, scratchableTexture);
        
        // 创建一个Sprite并赋给Image
        Sprite sprite = Sprite.Create(scratchableTexture, new Rect(0, 0, scratchableTexture.width, scratchableTexture.height), 
            new Vector2(0.5f, 0.5f));
        maskImage.sprite = sprite;

        rectTransform = maskImage.GetComponent<RectTransform>();
    }

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            Vector2 localPoint;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
                    rectTransform, Input.mousePosition, Camera, out localPoint))
            {
                // 转换 localPoint.x 到 [0, 1] 范围,然后映射到纹理宽度范围 [0, scratchableTexture.width]
                float x = (localPoint.x / rectTransform.rect.width + 0.5f) * scratchableTexture.width;

                // 转换 localPoint.y 到 [0, 1] 范围,然后映射到纹理高度范围 [0, scratchableTexture.height]
                float y = (localPoint.y / rectTransform.rect.height + 0.5f) * scratchableTexture.height;
                
                ScratchAt((int)x, (int)y);
            }
        }
    }

    //擦除
    private void ScratchAt(int x, int y)
    {
        if (scratchableTexture == null) return;

        int startX = Mathf.Clamp(x - (int)(brushSize / 2), 0, scratchableTexture.width);
        int startY = Mathf.Clamp(y - (int)(brushSize / 2), 0, scratchableTexture.height);
        int endX = Mathf.Clamp(x + (int)(brushSize / 2), 0, scratchableTexture.width);
        int endY = Mathf.Clamp(y + (int)(brushSize / 2), 0, scratchableTexture.height);

        for (int i = startX; i < endX; i++)
        {
            for (int j = startY; j < endY; j++)
            {
                float dist = Vector2.Distance(new Vector2(i, j), new Vector2(x, y));
                if (dist < brushSize / 2)
                {
                    scratchableTexture.SetPixel(i, j, Color.clear);
                }
            }
        }

        scratchableTexture.Apply();
    }
}

扩展讲解:

在 Unity 中,RectTransformUtility.ScreenPointToLocalPointInRectangle 方法将屏幕坐标转换为相对于 RectTransform 的局部坐标。然而,这些局部坐标是相对于 RectTransform 的中心点的,即 (0, 0) 代表 RectTransform 的中心。因此,我们需要进一步将这些局部坐标转换为纹理的坐标,以便正确地擦除图片上的像素。

假设我们的 RectTransform 范围从左下角 (-width/2, -height/2) 到右上角 (width/2, height/2),而纹理坐标范围从 (0, 0) 到 (width, height),我们需要进行以下步骤:

1.将局部坐标转换为 [0, 1] 范围:

   (1). 局部坐标 localPoint.x 和 localPoint.y 在范围 [-width/2, width/2] 和 [-height/2, height/2]。
   (2). 我们需要将它们偏移和缩放到 [0, 1] 范围。具体来说,将 localPoint.x 转换为 [0, 1] 范围,可以通过 (localPoint.x / rectTransform.rect.width + 0.5f) 实现;同理,对 localPoint.y 进行转换。


2.将 [0, 1] 范围映射到纹理坐标范围 [0, textureWidth] 和 [0, textureHeight]:

      (1). 将 [0, 1] 范围乘以纹理的宽度或高度,得到实际的纹理坐标。

// 转换 localPoint.x 到 [0, 1] 范围,然后映射到纹理宽度范围 [0, scratchableTexture.width]
float x = (localPoint.x / rectTransform.rect.width + 0.5f) * scratchableTexture.width;

// 转换 localPoint.y 到 [0, 1] 范围,然后映射到纹理高度范围 [0, scratchableTexture.height]
float y = (localPoint.y / rectTransform.rect.height + 0.5f) * scratchableTexture.height;

具体来说:

1. localPoint.x / rectTransform.rect.width 将 localPoint.x 从 [-width/2, width/2] 范围转换为 [-0.5, 0.5] 范围。
2. 加上 0.5 (+ 0.5f) 将其转换为 [0, 1] 范围。
3. 乘以 scratchableTexture.width 将其映射到纹理的实际宽度范围 [0, textureWidth]。
4. 对 localPoint.y 进行类似的操作,将其转换到纹理的实际高度范围 [0, textureHeight]。


这样,我们就成功地将鼠标在 RectTransform 上的局部坐标转换为 Texture2D 的纹理坐标,从而使刮刮乐效果能够准确反映鼠标的位置。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值