Files
aibis-dream/Assets/Scripts/UI/Panel/SignBoard.cs
T
2025-11-17 16:23:41 +08:00

281 lines
9.2 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AibisDream
{
/// <summary>
/// 画板组件,支持鼠标拖动绘制(UI Space)
/// 需要挂载在带有RectTransform和RawImage的GameObject上
/// </summary>
[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<RawImage>();
rectTransform = GetComponent<RectTransform>();
}
private void Start()
{
InitializeTexture();
}
/// <summary>
/// 初始化画布纹理
/// </summary>
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;
}
/// <summary>
/// 将屏幕坐标转换为纹理坐标
/// </summary>
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;
}
/// <summary>
/// 在指定位置绘制
/// </summary>
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);
}
}
}
}
}
/// <summary>
/// 在两点之间绘制线条(平滑绘制)
/// </summary>
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);
}
}
/// <summary>
/// 应用绘制更改到纹理
/// </summary>
private void ApplyDrawing()
{
if (drawTexture != null)
{
drawTexture.Apply();
// 确保RawImage显示更新
if (drawImage != null && drawImage.texture != drawTexture)
{
drawImage.texture = drawTexture;
}
}
}
/// <summary>
/// 清除画布
/// </summary>
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();
}
/// <summary>
/// 设置画笔颜色
/// </summary>
public void SetDrawColor(Color color)
{
drawColor = color;
}
/// <summary>
/// 设置画笔大小
/// </summary>
public void SetBrushSize(int size)
{
brushSize = Mathf.Max(1, size);
}
/// <summary>
/// 设置背景颜色
/// </summary>
public void SetBackgroundColor(Color color)
{
backgroundColor = color;
}
/// <summary>
/// 保存画布为PNG图片
/// </summary>
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);
}
}
}
}