我所实现的橡皮擦效果是设置图片某点的像素的透明度为0,来简单实现擦除效果的;
下面是效果
首先需要注意两点:1:设置 Main Camera 的 projection 属性为Orthographic
2:设置Canvas 的Render Mode 为 Screen Space - Camera
然后找一张图片,导入Unity 中并修改它的读写权限,创建Raw Imager
这样启动之后就可以测试效果了。
附上代码:(代码中有解释)
1 using AYUI.UIFrame;
2 using DG.Tweening;
3 using System;
4 using System.Collections;
5 using System.Collections.Generic;
6 using UnityEngine;
7 using UnityEngine.EventSystems;
8 using UnityEngine.UI;
9
10 public class EraseMask : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
11 {
12
13 //是否擦除了
14 public bool isStartEraser;
15
16 //是否擦除结束了
17 public bool isEndEraser;
18
19 //开始事件
20 public Action eraserStartEvent;
21
22 //结束事件
23 public Action eraserEndEvent;
24
25 public RawImage uiTex;
26 Texture2D tex;
27 Texture2D MyTex;
28 int mWidth;
29 int mHeight;
30
31 [Header("Brush Size")]
32 public int brushSize = 50;
33
34 [Header("Rate")]
35 public int rate = 90;
36
37 float maxColorA;
38 float colorA;
39
40 void Awake()
41 {
42 tex = (Texture2D)uiTex.mainTexture;
43 MyTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
44 mWidth = MyTex.width;
45 mHeight = MyTex.height;
46
47 MyTex.SetPixels(tex.GetPixels());
48 MyTex.Apply();
49 uiTex.texture = MyTex;
50 maxColorA = MyTex.GetPixels().Length;
51 colorA = 0;
52 isEndEraser = false;
53 isStartEraser = false;
54
55 }
56
57
58 /// <summary>
59 /// 贝塞尔平滑
60 /// </summary>
61 /// <param name="start">起点</param>
62 /// <param name="mid">中点</param>
63 /// <param name="end">终点</param>
64 /// <param name="segments">段数</param>
65 /// <returns></returns>
66 public Vector2[] Beizier(Vector2 start, Vector2 mid, Vector2 end, int segments)
67 {
68 float d = 1f / segments;
69 Vector2[] points = new Vector2[segments - 1];
70 for (int i = 0; i < points.Length; i++)
71 {
72 float t = d * (i + 1);
73 points[i] = (1 - t) * (1 - t) * mid + 2 * t * (1 - t) * start + t * t * end;
74 }
75 List<Vector2> rps = new List<Vector2>();
76 rps.Add(mid);
77 rps.AddRange(points);
78 rps.Add(end);
79 return rps.ToArray();
80 }
81
82
83
84 bool startDraw = false;
85 bool twoPoints = false;
86 Vector2 lastPos;//最后一个点
87 Vector2 penultPos;//倒数第二个点
88 float radius = 12f;
89 float distance = 1f;
90
91
92
93 #region 事件
94 public void OnPointerDown(PointerEventData eventData)
95 {
96 if (isEndEraser) { return; }
97 startDraw = true;
98 penultPos = eventData.position;
99 CheckPoint(penultPos);
100 }
101
102 public void OnDrag(PointerEventData eventData)
103 {
104 if (isEndEraser) { return; }
105 if (twoPoints && Vector2.Distance(eventData.position, lastPos) > distance)//如果两次记录的鼠标坐标距离大于一定的距离,开始记录鼠标的点
106 {
107 Vector2 pos = eventData.position;
108 float dis = Vector2.Distance(lastPos, pos);
109
110 CheckPoint(eventData.position);
111 int segments = (int)(dis / radius);//计算出平滑的段数
112 segments = segments < 1 ? 1 : segments;
113 if (segments >= 10) { segments = 10; }
114 Vector2[] points = Beizier(penultPos, lastPos, pos, segments);//进行贝塞尔平滑
115 for (int i = 0; i < points.Length; i++)
116 {
117 CheckPoint(points[i]);
118 }
119 lastPos = pos;
120 if (points.Length > 2)
121 penultPos = points[points.Length - 2];
122 }
123 else
124 {
125 twoPoints = true;
126 lastPos = eventData.position;
127 }
128 }
129
130 public void OnPointerUp(PointerEventData eventData)
131 {
132 if (isEndEraser) { return; }
133 //CheckPoint(eventData.position);
134 startDraw = false;
135 twoPoints = false;
136 }
137
138
139 #endregion
140
141
142
143 void CheckPoint(Vector3 pScreenPos)
144 {
145 Vector3 worldPos = Camera.main.ScreenToWorldPoint(pScreenPos);
146 Vector3 localPos = uiTex.gameObject.transform.InverseTransformPoint(worldPos);
147
148 if (localPos.x > -mWidth / 2 && localPos.x < mWidth / 2 && localPos.y > -mHeight / 2 && localPos.y < mHeight / 2)
149 {
150 for (int i = (int)localPos.x - brushSize; i < (int)localPos.x + brushSize; i++)
151 {
152 for (int j = (int)localPos.y - brushSize; j < (int)localPos.y + brushSize; j++)
153 {
154 if (Mathf.Pow(i - localPos.x, 2) + Mathf.Pow(j - localPos.y, 2) > Mathf.Pow(brushSize, 2))
155 continue;
156 if (i < 0) { if (i < -mWidth / 2) { continue; } }
157 if (i > 0) { if (i > mWidth / 2) { continue; } }
158 if (j < 0) { if (j < -mHeight / 2) { continue; } }
159 if (j > 0) { if (j > mHeight / 2) { continue; } }
160
161 Color col = MyTex.GetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2);
162 if (col.a != 0f)
163 {
164 col.a = 0.0f;
165 colorA++;
166 MyTex.SetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2, col);
167 }
168 }
169 }
170
171
172 //开始刮的时候 去判断进度
173 if (!isStartEraser)
174 {
175 isStartEraser = true;
176 InvokeRepeating("getTransparentPercent", 0f, 0.2f);
177 if (eraserStartEvent != null)
178 eraserStartEvent.Invoke();
179 }
180
181 MyTex.Apply();
182 }
183 }
184
185
186
187 double fate;
188
189
190 /// <summary>
191 /// 检测当前刮刮卡 进度
192 /// </summary>
193 /// <returns></returns>
194 public void getTransparentPercent()
195 {
196 if (isEndEraser) { return; }
197
198
199 fate = colorA / maxColorA * 100;
200
201 fate = (float)Math.Round(fate, 2);
202
203 // Debug.LogError("当前百分比: " + fate);
204
205 if (fate >= rate)
206 {
207 isEndEraser = true;
208 CancelInvoke("getTransparentPercent");
209 gameObject.SetActive(false);
210
211 //触发结束事件
212 if (eraserEndEvent != null)
213 eraserEndEvent.Invoke();
214
215 }
216 }
217
218 }
知识兔View Code另附一张好用的图片 :https://pan.baidu.com/s/1Z_GTuwPouFtiSgVcFfrAcg
提取码:da7t