using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace AibisDream { /// /// 画板组件,支持鼠标拖动绘制(UI Space) /// 需要挂载在带有RectTransform和RawImage的GameObject上 /// [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RawImage))] public class SignBoard : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler { [Header("画板设置")] [SerializeField] private int textureWidth = 512; [SerializeField] private int textureHeight = 512; [SerializeField] private Color backgroundColor = Color.white; [Header("画笔设置")] [SerializeField] private Color drawColor = Color.black; [SerializeField] private int brushSize = 5; [SerializeField] private bool smoothDrawing = true; // 是否平滑绘制(连接两点之间的所有点) private Texture2D drawTexture; private RawImage drawImage; private RectTransform rectTransform; private Vector2 lastDrawPoint; private bool isDrawing = false; private void Awake() { drawImage = GetComponent(); rectTransform = GetComponent(); } private void Start() { InitializeTexture(); } /// /// 初始化画布纹理 /// private void InitializeTexture() { // 创建可读写的纹理 drawTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false); drawTexture.filterMode = FilterMode.Bilinear; // 填充背景色 Color[] pixels = new Color[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } drawTexture.SetPixels(pixels); drawTexture.Apply(); // 设置到RawImage drawImage.texture = drawTexture; } /// /// 将屏幕坐标转换为纹理坐标 /// 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) { 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++) { // 检查是否在圆形范围内 float distance = Mathf.Sqrt(x * x + y * y); if (distance <= brushSize) { int px = centerX + x; int py = centerY + y; // 检查边界 if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight) { // 根据距离计算透明度,实现柔和的笔刷效果 float alpha = 1f - (distance / brushSize); Color finalColor = drawColor; finalColor.a *= alpha; // 混合颜色 Color existingColor = drawTexture.GetPixel(px, py); Color blendedColor = Color.Lerp(existingColor, finalColor, finalColor.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() { Color[] pixels = new Color[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } drawTexture.SetPixels(pixels); drawTexture.Apply(); } /// /// 设置画笔颜色 /// public void SetDrawColor(Color color) { drawColor = color; } /// /// 设置画笔大小 /// public void SetBrushSize(int size) { brushSize = Mathf.Max(1, size); } /// /// 设置背景颜色 /// public void SetBackgroundColor(Color color) { backgroundColor = color; } /// /// 保存画布为PNG图片 /// public byte[] SaveToPNG() { return drawTexture.EncodeToPNG(); } // EventSystem接口实现 public void OnPointerDown(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { isDrawing = true; // 确保第一次点击也能绘制 lastDrawPoint = textureCoord; DrawAtPoint(textureCoord); ApplyDrawing(); // 强制更新RawImage显示 if (drawImage != null && drawTexture != null) { drawImage.texture = drawTexture; } } } public void OnDrag(PointerEventData eventData) { if (!isDrawing) return; if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { if (smoothDrawing) { // 平滑绘制:连接两点之间的所有点 // 如果两点距离太近,直接绘制当前点(避免DrawLine计算出错) float distance = Vector2.Distance(lastDrawPoint, textureCoord); if (distance < 0.1f) { DrawAtPoint(textureCoord); } else { DrawLine(lastDrawPoint, textureCoord); } } else { // 直接绘制当前点 DrawAtPoint(textureCoord); } ApplyDrawing(); lastDrawPoint = textureCoord; } } public void OnPointerUp(PointerEventData eventData) { isDrawing = false; } private void OnDestroy() { // 清理纹理资源 if (drawTexture != null) { Destroy(drawTexture); } } } }