Unity 实现橡皮擦效果
我所实现的橡皮擦效果是设置图片某点的像素的透明度为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 }
另附一张好用的图片 :https://pan.baidu.com/s/1Z_GTuwPouFtiSgVcFfrAcg
提取码:da7t