当前位置: 首页 > news >正文

unity UGUI 鼠标画线

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using UnityEngine.UI;
/*
使用方法:
在场景中新建一个空的 GameObject(右键 -> UI -> 空对象,或直接创建空对象后添加 RectTransform 组件)
给这个新对象挂载 LineDrawer 脚本(此时会自动添加 CanvasRenderer 组件,无需手动添加 Image)
调整该对象的 RectTransform 大小和位置,使其覆盖你需要绘制的区域
*/
[RequireComponent(typeof(CanvasRenderer))]
public class LineDrawer : MaskableGraphic, IPointerDownHandler, IDragHandler, IPointerUpHandler
{[Header("线段的宽度")][Tooltip("线段的宽度,单位为像素。值越大,绘制的线条越粗。建议取值范围:1-20")][SerializeField] private float lineWidth = 5f;[Header("线段的填充颜色")][Tooltip("通过调整RGBA值可以改变线条的颜色和透明度")][SerializeField] private Color lineColor = Color.black;[Header("最小距离阈值")][Tooltip("鼠标拖动时添加新点的最小距离阈值(像素)。当鼠标移动距离超过此值时才会添加新点,值越小线条越精确但点数量越多,过小将影响性能")][SerializeField] private float minSegmentDistance = 5f;[Header("平滑处理")][Tooltip("是否启用贝塞尔曲线平滑处理。勾选后线条会更流畅自然,不勾选则为直线段连接")][SerializeField] private bool drawSmoothLines = true;[Header("平滑精细度")][Tooltip("平滑线条的精细程度,控制贝塞尔曲线的分段数量。值越大曲线越平滑但性能消耗增加,建议取值范围:3-10,仅在启用平滑线条时生效")][SerializeField] private int smoothness = 5;[Header("多线段模式")][Tooltip("勾选后可以绘制任意数量的独立线段,它们会同时显示;取消勾选则每次鼠标按下会清除之前所有线条,只显示当前正在绘制的单一线段")][SerializeField] private bool multiLineMode = true;// 线段类,存储一条线段的所有点、颜色和粗细private class Line{public List<Vector2> points = new List<Vector2>();public Color color;public float width;}private Line currentLine = null;private List<Line> allLines = new List<Line>();private bool isDrawing = false;// 重写颜色属性public override Color color{get => lineColor;set{lineColor = value;SetVerticesDirty();}}// 线段粗细属性public float LineWidth{get => lineWidth;set{lineWidth = Mathf.Max(0.1f, value);// 更新当前正在绘制的线段(如果存在)if (isDrawing && currentLine != null){currentLine.width = lineWidth;SetVerticesDirty();}}}protected override void OnPopulateMesh(VertexHelper vh){vh.Clear();// 绘制所有已完成的线段foreach (var line in allLines){if (line.points.Count < 2) continue;DrawLine(vh, line);}// 绘制当前正在绘制的线段if (currentLine != null && currentLine.points.Count >= 2){DrawLine(vh, currentLine);}}// 绘制单条线段private void DrawLine(VertexHelper vh, Line line){List<Vector2> pointsToDraw = line.points;// 如果需要平滑线段,应用贝塞尔曲线if (drawSmoothLines && line.points.Count > 2){pointsToDraw = ApplySmoothing(line.points);}// 绘制线段DrawLineSegments(vh, pointsToDraw, line.color, line.width);}// 应用平滑处理private List<Vector2> ApplySmoothing(List<Vector2> points){List<Vector2> smoothedPoints = new List<Vector2>();for (int i = 0; i < points.Count - 1; i++){Vector2 start = points[i];Vector2 end = points[i + 1];Vector2 control1 = i > 0 ? points[i] : start;Vector2 control2 = i < points.Count - 2 ? points[i + 1] : end;for (int j = 0; j <= smoothness; j++){float t = j / (float)smoothness;smoothedPoints.Add(BezierCurve(start, control1, control2, end, t));}}return smoothedPoints;}// 绘制线段(带独立粗细参数)private void DrawLineSegments(VertexHelper vh, List<Vector2> points, Color color, float lineWidth){int count = points.Count;if (count < 2) return;float halfWidth = lineWidth * 0.5f;// 存储所有计算出的顶点List<Vector2> vertices = new List<Vector2>();for (int i = 0; i < count; i++){if (i == 0){// 处理第一个点AddFirstPointVertices(vertices, points[i], points[i + 1], halfWidth);}else if (i == count - 1){// 处理最后一个点AddLastPointVertices(vertices, points[i - 1], points[i], halfWidth);}else{// 处理中间点AddMidPointVertices(vertices, points[i - 1], points[i], points[i + 1], halfWidth);}}// 添加顶点到VertexHelperint startVertexIndex = vh.currentVertCount;for (int i = 0; i < vertices.Count; i++){// 计算UV,这里简单处理为0-1范围float uvX = Mathf.InverseLerp(0, rectTransform.rect.width, vertices[i].x);float uvY = Mathf.InverseLerp(0, rectTransform.rect.height, vertices[i].y);vh.AddVert(vertices[i], color, new Vector2(uvX, uvY));}// 添加三角形for (int i = 0; i < vertices.Count - 2; i += 2){vh.AddTriangle(startVertexIndex + i, startVertexIndex + i + 1, startVertexIndex + i + 3);vh.AddTriangle(startVertexIndex + i, startVertexIndex + i + 3, startVertexIndex + i + 2);}}// 计算贝塞尔曲线上的点private Vector2 BezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t){float u = 1 - t;float tt = t * t;float uu = u * u;float uuu = uu * u;float ttt = tt * t;Vector2 p = uuu * p0;p += 3 * uu * t * p1;p += 3 * u * tt * p2;p += ttt * p3;return p;}// 添加第一个点的顶点(带粗细参数)private void AddFirstPointVertices(List<Vector2> vertices, Vector2 start, Vector2 next, float halfWidth){Vector2 dir = next - start;Vector2 normal = new Vector2(-dir.y, dir.x).normalized;vertices.Add(start + normal * halfWidth);vertices.Add(start - normal * halfWidth);}// 添加最后一个点的顶点(带粗细参数)private void AddLastPointVertices(List<Vector2> vertices, Vector2 prev, Vector2 end, float halfWidth){Vector2 dir = end - prev;Vector2 normal = new Vector2(-dir.y, dir.x).normalized;vertices.Add(end + normal * halfWidth);vertices.Add(end - normal * halfWidth);}// 添加中间点的顶点(带粗细参数)private void AddMidPointVertices(List<Vector2> vertices, Vector2 prev, Vector2 current, Vector2 next, float halfWidth){Vector2 dir1 = current - prev;Vector2 dir2 = next - current;Vector2 normal1 = new Vector2(-dir1.y, dir1.x).normalized;Vector2 normal2 = new Vector2(-dir2.y, dir2.x).normalized;// 计算平均法线Vector2 avgNormal = (normal1 + normal2).normalized;// 计算角度float angle = Vector2.Angle(normal1, normal2) * Mathf.Deg2Rad * 0.5f;float radiusMultiplier = 1 / Mathf.Cos(angle);vertices.Add(current + avgNormal * halfWidth * radiusMultiplier);vertices.Add(current - avgNormal * halfWidth * radiusMultiplier);}// 鼠标按下开始画线public void OnPointerDown(PointerEventData eventData){if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){// 如果不是多线段模式,清除所有线段if (!multiLineMode){allLines.Clear();}// 开始新的线段currentLine = new Line();currentLine.points.Add(localPos);currentLine.color = lineColor; // 使用当前颜色currentLine.width = lineWidth; // 使用当前粗细isDrawing = true;SetVerticesDirty();}}// 鼠标拖动时添加点public void OnDrag(PointerEventData eventData){if (!isDrawing || currentLine == null) return;if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){// 只在距离足够远时添加新点if (Vector2.Distance(currentLine.points[currentLine.points.Count - 1], localPos) > minSegmentDistance){currentLine.points.Add(localPos);SetVerticesDirty();}}}// 鼠标抬起结束画线public void OnPointerUp(PointerEventData eventData){if (!isDrawing || currentLine == null) return;isDrawing = false;// 确保最后添加终点if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){if (currentLine.points.Count == 1 || Vector2.Distance(currentLine.points[currentLine.points.Count - 1], localPos) > minSegmentDistance * 0.5f){currentLine.points.Add(localPos);}}// 如果当前线段有足够的点,添加到所有线段列表中if (currentLine.points.Count >= 2){allLines.Add(currentLine);}// 清空当前线段currentLine = null;SetVerticesDirty();}// 清除所有线条public void ClearAllLines(){allLines.Clear();currentLine = null;SetVerticesDirty();}// 设置特定线段的粗细public void SetLineWidth(int lineIndex, float width){if (lineIndex >= 0 && lineIndex < allLines.Count){allLines[lineIndex].width = Mathf.Max(0.1f, width);SetVerticesDirty();}}// 获取特定线段的粗细public float GetLineWidth(int lineIndex){if (lineIndex >= 0 && lineIndex < allLines.Count){return allLines[lineIndex].width;}return lineWidth;}// 获取线段数量public int GetLineCount(){return allLines.Count;}// 重写材质获取,使用默认UI材质public override Material material{get => defaultMaterial;set => base.material = value;}
}

参考:

https://blog.csdn.net/sdhexu/article/details/126593171?spm=1001.2014.3001.5502


文章转载自:

http://EJdH1Pe9.bpmnL.cn
http://9ab7Ycth.bpmnL.cn
http://jUnVpNZR.bpmnL.cn
http://FDJ06l9l.bpmnL.cn
http://71J36HPv.bpmnL.cn
http://cTI9ulH7.bpmnL.cn
http://bLimSx37.bpmnL.cn
http://4LPvI7HV.bpmnL.cn
http://7tjMoZ5o.bpmnL.cn
http://ytylcbVd.bpmnL.cn
http://YgzoZAhe.bpmnL.cn
http://nMFcrW0g.bpmnL.cn
http://Y38Pe46y.bpmnL.cn
http://nddD3TkB.bpmnL.cn
http://L4j3vT4s.bpmnL.cn
http://YzxDVVhE.bpmnL.cn
http://X4qLyyhU.bpmnL.cn
http://2Q4BR3F0.bpmnL.cn
http://TJ9GROLk.bpmnL.cn
http://60SXs5yd.bpmnL.cn
http://pLT4O3ao.bpmnL.cn
http://dh13ar4X.bpmnL.cn
http://3p4WkGzD.bpmnL.cn
http://gV1p1qZ8.bpmnL.cn
http://6L5tk62W.bpmnL.cn
http://b76E9rrM.bpmnL.cn
http://ZEcAFVtj.bpmnL.cn
http://vQQnBKOO.bpmnL.cn
http://XAVTDLOG.bpmnL.cn
http://WV1P0xaP.bpmnL.cn
http://www.dtcms.com/a/378353.html

相关文章:

  • ALBEF(Align Before Fuse)
  • redis 集群——redis cluster(去中心化)
  • k8s部署kafka三节点集群
  • 11.ImGui-加载字体和中文
  • 大模型推理革命
  • 项目-sqlite类的实现
  • 物联网领域中PHP框架的最佳选择有哪些?
  • ARM1.(ARM体系结构)
  • Linux开机启动设置全攻略
  • 解决Pytest参数化测试中文显示乱码问题:两种高效方法
  • PHP弱类型比较在CTF比赛中的深入分析与实战应用
  • 科大讯飞一面
  • html块标签和内联标签的通俗理解
  • 【C++】STL--Vector使用极其模拟实现
  • QT子线程与GUI线程安全交互
  • 论 Intel CPU 进化史:德承工控机全面进化 搭载新一代 Intel® Core™ Ultra 7/5/3 处理器
  • 论文阅读/博弈论/拍卖:《Truthful Auction for Cooperative Communications》
  • 【论文阅读】Towards Privacy-Enhanced and Robust Clustered Federated Learning
  • [论文阅读] 告别“数量为王”:双轨道会议模型+LS,破解AI时代学术交流困局
  • 【UE】2D SphereNormalsMap - 实时计算2D “球形法线” 贴图
  • 保护模式下的特权级_考研倒计时 100 days
  • 中科米堆CASAIM高精度蓝光3D扫描激光抄数服务逆向三维建模
  • 【Canvas与几何图案】六钩内嵌大卫之星黑白图案
  • 智能体工作流画布:提升企业业务流程自动化效率
  • 如何从 iPhone 打印联系人信息
  • FOC系列(六)----学习DRV8313/MS8313芯片,绘制驱动板
  • Android开发值Android官方模拟器启动失败问题跟踪排查
  • hardhat 项目目录介绍
  • IROS 2025 多智能体深度强化学习算法实现Crazyflie无人机在复杂环境中协同追逐
  • 光平面标定 (Laser Plane Calibration) 的原理和流程