初始时的样子
拖动擦除,露出背景的白色
这里用了一个父物体,下面设置两张图,一张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);
}
}