using System; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; namespace AibisDream { /// /// 画板组件,支持鼠标拖动绘制(UI Space) /// 需要挂载在带有RectTransform和RawImage的GameObject上 /// [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RawImage))] public class SignArea : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler { [Header("画板设置")] [SerializeField] private int textureWidth = 512; [SerializeField] private int textureHeight = 512; [SerializeField] private Color32 backgroundColor = Color.clear; [Header("画笔设置")] [SerializeField] private Color drawColor = Color.black; [SerializeField] private int brushSize = 5; [SerializeField] private bool smoothDrawing = true; // 是否平滑绘制(连接两点之间的所有点) [Header("长度目标设置")] [SerializeField] private float targetLength = 100f; // 目标绘画长度(像素) public event Action onLengthTargetExceeded; // 绘画长度超过目标事件(参数:当前长度) private Texture2D drawTexture; private RawImage drawImage; private RectTransform rectTransform; private Vector2 lastDrawPoint; private bool isDrawing = false; private float currentStrokeLength = 0f; // 当前单次绘画的线段长度 private float totalLength = 0f; // 累计所有笔画的总长度 private bool hasTriggeredTargetEvent = false; // 是否已触发超过目标事件 private bool isLocked = true; // 锁定标记,锁定状态下无法绘画 private void Awake() { drawImage = GetComponent(); rectTransform = GetComponent(); } /// /// 初始化画布纹理 /// public void Init() { // 创建可读写的纹理 drawTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false); drawTexture.filterMode = FilterMode.Point; // 使用 Point 过滤模式实现像素化效果 // 填充背景色 Color32[] pixels = new Color32[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } drawTexture.SetPixels32(pixels); drawTexture.Apply(); // 设置到RawImage drawImage.texture = drawTexture; // 重置累计总长度和事件标志 totalLength = 0f; hasTriggeredTargetEvent = false; } public void Dispose() { Clear(); if (drawTexture != null) { Destroy(drawTexture); } drawTexture = null; lastDrawPoint = Vector2.zero; isDrawing = false; onLengthTargetExceeded = null; // Dispose 后锁定 isLocked = true; } /// /// 将屏幕坐标转换为纹理坐标 /// private bool ScreenToTextureCoord(Vector2 screenPos, out Vector2 textureCoord) { textureCoord = Vector2.zero; // 将屏幕坐标转换为UI本地坐标 if (UIManager.Instance.ScreenPointToLocalPointInRectangle(rectTransform, screenPos, out Vector2 localPoint)) { // 检查是否在RectTransform范围内 Rect rect = rectTransform.rect; if (!rect.Contains(localPoint)) { return false; } // 转换为纹理坐标 (0,0) 到 (textureWidth, textureHeight) // localPoint的范围是 (-rect.width/2, -rect.height/2) 到 (rect.width/2, rect.height/2) float normalizedX = (localPoint.x + rect.width / 2) / rect.width; float normalizedY = (localPoint.y + rect.height / 2) / rect.height; textureCoord.x = normalizedX * textureWidth; textureCoord.y = normalizedY * textureHeight; return true; } return false; } /// /// 在指定位置绘制(方形像素笔刷) /// private void DrawAtPoint(Vector2 point) { // 检查纹理是否已初始化 if (drawTexture == null) { Debug.LogWarning("SignArea: drawTexture 未初始化,请先调用 Init() 方法"); return; } int centerX = Mathf.RoundToInt(point.x); int centerY = Mathf.RoundToInt(point.y); // 绘制方形笔刷(像素化,无柔边效果) for (int x = -brushSize; x <= brushSize; x++) { for (int y = -brushSize; y <= brushSize; y++) { int px = centerX + x; int py = centerY + y; // 检查边界 if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight) { // 像素化绘制:直接使用画笔颜色,不进行透明度渐变 Color existingColor = drawTexture.GetPixel(px, py); // Alpha 混合公式: result = src * srcAlpha + dst * (1 - srcAlpha) Color blendedColor = new Color( Mathf.Lerp(existingColor.r, drawColor.r, drawColor.a), Mathf.Lerp(existingColor.g, drawColor.g, drawColor.a), Mathf.Lerp(existingColor.b, drawColor.b, drawColor.a), Mathf.Min(1f, existingColor.a + drawColor.a * (1f - existingColor.a)) ); drawTexture.SetPixel(px, py, blendedColor); } } } } /// /// 在两点之间绘制线条(平滑绘制) /// private void DrawLine(Vector2 start, Vector2 end) { float distance = Vector2.Distance(start, end); int steps = Mathf.CeilToInt(distance); for (int i = 0; i <= steps; i++) { float t = steps > 0 ? (float)i / steps : 0; Vector2 point = Vector2.Lerp(start, end, t); DrawAtPoint(point); } } /// /// 应用绘制更改到纹理 /// private void ApplyDrawing() { if (drawTexture != null) { drawTexture.Apply(); // 确保RawImage显示更新 if (drawImage != null && drawImage.texture != drawTexture) { drawImage.texture = drawTexture; } } } /// /// 清除画布 /// public void Clear() { if (drawTexture == null) { Debug.LogWarning("SignArea: drawTexture 未初始化,无法清除画布"); return; } Color32[] pixels = new Color32[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } drawTexture.SetPixels32(pixels); drawTexture.Apply(); // 重置累计总长度和事件标志 totalLength = 0f; hasTriggeredTargetEvent = false; onLengthTargetExceeded = null; } /// /// 设置画笔颜色 /// public void SetDrawColor(Color color) { drawColor = color; } /// /// 设置画笔大小 /// public void SetBrushSize(int size) { brushSize = Mathf.Max(1, size); } /// /// 设置背景颜色 /// public void SetBackgroundColor(Color color) { backgroundColor = color; } /// /// 设置目标绘画长度 /// public void SetTargetLength(float length) { targetLength = Mathf.Max(0f, length); } /// /// 获取累计所有笔画的总长度 /// public float GetCurrentStrokeLength() { return totalLength; } /// /// 设置锁定状态 /// /// true 为锁定(无法绘画),false 为解锁(可以绘画) public void SetLocked(bool locked) { isLocked = locked; // 如果锁定,停止当前绘画 if (locked && isDrawing) { isDrawing = false; } } /// /// 获取当前锁定状态 /// public bool IsLocked() { return isLocked; } /// /// 保存画布为PNG图片 /// public byte[] SaveToPNG() { if (drawTexture == null) { Debug.LogWarning("SignArea: drawTexture 未初始化,无法保存图片"); return null; } return drawTexture.EncodeToPNG(); } // EventSystem接口实现 public void OnPointerDown(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; // 检查是否锁定 if (isLocked) { return; } // 检查纹理是否已初始化 if (drawTexture == null) { Debug.LogWarning("SignArea: drawTexture 未初始化,请先调用 Init() 方法"); return; } if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { isDrawing = true; // 重置当前绘画会话的累计长度 currentStrokeLength = 0f; // 确保第一次点击也能绘制 lastDrawPoint = textureCoord; DrawAtPoint(textureCoord); ApplyDrawing(); // 强制更新RawImage显示 if (drawImage != null && drawTexture != null) { drawImage.texture = drawTexture; } } } public void OnDrag(PointerEventData eventData) { if (!isDrawing) return; // 检查是否锁定 if (isLocked) { isDrawing = false; return; } if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { float segmentLength = Vector2.Distance(lastDrawPoint, textureCoord); if (smoothDrawing) { // 平滑绘制:连接两点之间的所有点 // 如果两点距离太近,直接绘制当前点(避免DrawLine计算出错) if (segmentLength < 0.1f) { DrawAtPoint(textureCoord); } else { DrawLine(lastDrawPoint, textureCoord); // 累计线段长度 currentStrokeLength += segmentLength; } } else { // 直接绘制当前点,但仍累计线段长度 DrawAtPoint(textureCoord); // 累计线段长度 currentStrokeLength += segmentLength; } ApplyDrawing(); lastDrawPoint = textureCoord; } } public void OnPointerUp(PointerEventData eventData) { if (isDrawing) { // 将当前笔画长度累加到总长度 totalLength += currentStrokeLength; // 检查累计总长度是否超过目标长度,且尚未触发过事件 if (!hasTriggeredTargetEvent && totalLength > targetLength) { hasTriggeredTargetEvent = true; // 触发超过目标长度事件,传递累计总长度 onLengthTargetExceeded?.Invoke(totalLength); // 触发事件后自动锁定 isLocked = true; } } isDrawing = false; } private void OnDestroy() { // 清理纹理资源 if (drawTexture != null) { Destroy(drawTexture); } } } }