最终效果
代码部分
using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ScratchTest : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
public bool isStartEraser;
public bool isEndEraser;
public Action eraserStartEvent;
public Action eraserEndEvent;
public RawImage uiTex;
Texture2D tex;
Texture2D MyTex;
int mWidth;
int mHeight;
[Header("笔刷大小")]
public int brushSize = 50;
[Header("刮刮乐比例")]
public int rate = 90;
float maxColorA;
float colorA;
void Awake()
{
tex = (Texture2D)uiTex.mainTexture;
MyTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
mWidth = MyTex.width;
mHeight = MyTex.height;
MyTex.SetPixels(tex.GetPixels());
MyTex.Apply();
uiTex.texture = MyTex;
maxColorA = MyTex.GetPixels().Length;
colorA = 0;
isEndEraser = false;
isStartEraser = false;
}
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();
}
bool startDraw = false;
bool twoPoints = false;
Vector2 lastPos;
Vector2 penultPos;
float radius = 12f;
float distance = 1f;
#region 事件
public void OnPointerDown(PointerEventData eventData)
{
if (isEndEraser) { return; }
startDraw = true;
penultPos = eventData.position;
CheckPoint(penultPos);
}
public void OnDrag(PointerEventData eventData)
{
if (isEndEraser) { return; }
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)
{
if (isEndEraser) { return; }
startDraw = false;
twoPoints = false;
}
#endregion
void CheckPoint(Vector3 pScreenPos)
{
Vector3 worldPos = Camera.main.ScreenToWorldPoint(pScreenPos);
Vector3 localPos = uiTex.gameObject.transform.InverseTransformPoint(worldPos);
if (localPos.x > -mWidth / 2 && localPos.x < mWidth / 2 && localPos.y > -mHeight / 2 && localPos.y < mHeight / 2)
{
for (int i = (int)localPos.x - brushSize; i < (int)localPos.x + brushSize; i++)
{
for (int j = (int)localPos.y - brushSize; j < (int)localPos.y + brushSize; j++)
{
if (Mathf.Pow(i - localPos.x, 2) + Mathf.Pow(j - localPos.y, 2) > Mathf.Pow(brushSize, 2))
continue;
if (i < 0) { if (i < -mWidth / 2) { continue; } }
if (i > 0) { if (i > mWidth / 2) { continue; } }
if (j < 0) { if (j < -mHeight / 2) { continue; } }
if (j > 0) { if (j > mHeight / 2) { continue; } }
Color col = MyTex.GetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2);
if (col.a != 0f)
{
col.a = 0.0f;
colorA++;
MyTex.SetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2, col);
}
}
}
if (!isStartEraser)
{
isStartEraser = true;
InvokeRepeating("getTransparentPercent", 0f, 0.2f);
if (eraserStartEvent != null)
eraserStartEvent.Invoke();
}
MyTex.Apply();
}
}
double fate;
public void getTransparentPercent()
{
if (isEndEraser) { return; }
fate = colorA / maxColorA * 100;
fate = (float)Math.Round(fate, 2);
if (fate >= rate)
{
isEndEraser = true;
CancelInvoke("getTransparentPercent");
uiTex.gameObject.SetActive(false);
if (eraserEndEvent != null)
eraserEndEvent.Invoke();
}
}
}
使用方法 Canvas设置为Screen Space-Camera,并指定UI相机。
准备一张底图和要刮的图,底图为Image,要刮的图为RawImage。(注意图片不要长宽比例变形,可以等比缩放,不然会影响刮的痕迹和判定)
将该脚本挂在Canvas下,并把RawImage拖拽给UiTex。 原文地址
|