This repository has been archived on 2026-07-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
UIGraphRenderer/Runtime/Pattern/UILineRenderer.cs
2026-04-30 08:57:45 +08:00

142 lines
5.1 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// UI上线条绘制组件
/// 支持设置线条厚度、居中显示,并为线段之间添加斜角边缘
/// 继承自 MaskableGraphic,可与 UI 遮罩系统配合使用
/// </summary>
[AddComponentMenu("Xeric Library/UI/UILineRenderer", 15)]
public class UILineRenderer : XericRendererComponent
{
// todo 根据优弧劣弧绘制线段,这么说可能不太准确,但我需要在90度以内绘制漂亮的转角,大于90度则用中心圆代替
#region 字段属性
/// <summary>
/// 线条的顶点数组,至少需要 2 个点才能绘制线条
/// 点的坐标基于本地空间,中心点由 center 属性控制
/// </summary>
public List<Vector2> points = new List<Vector2>();
private Vector2[] _points;
public float thickness = 10f;
public bool center = true;
public bool cycleLoop = false;
/// <summary>
/// 是否启用倒角功能
/// </summary>
public bool useChamfer = false;
/// <summary>
/// 倒角细分数量,控制圆弧的平滑度
/// </summary>
public int chamferSegments = 4;
/// <summary>
/// 内圆弧半径,当大于0时启用内圆弧
/// </summary>
public float innerRadius = 0f;
#endregion
/// <summary>
/// 添加顶点
/// </summary>
/// <param name="point"></param>
/// <param name="mergingRadius">合并弧度,指定一个弧度,在添加顶点时,自动合并之前的顶点为直线</param>
public void AddPoint(Vector2 point, float mergingRadius = -1)
{
if (mergingRadius > 0 && points.Count >= 2)
{
var a = points[^1];
var b = Vector3.Dot((a - points[^2]).normalized, (a - point).normalized);
if (b < mergingRadius)
points[^1] = point;
return;
}
points.Add(point);
Refresh();
}
public void RemovePoint(int index)
{
points.RemoveAt(index);
Refresh();
}
public void CleanPoint(Vector2 point)
{
points.Clear();
Refresh();
}
/// <summary>
/// 生成线条网格
/// </summary>
/// <param name="vh">用于构建网格的 VertexHelper 对象</param>
protected override void OnPopulateMesh(VertexHelper vh)
{
// 清空现有的网格数据
vh.Clear();
var offset = XericRendererUtils.SwitchCenterOffset(center, rectTransform.sizeDelta);
_points = points.ToArray();
if (useChamfer)
{
vh.PopulateLineByPointsArray_CFAL(_points, thickness, cycleLoop, color, offset, chamferSegments, innerRadius);
}
else
{
vh.PopulateLineByPointsArray_DSL5(_points, thickness, cycleLoop, color, offset);
}
return;
// 至少需要 2 个点才能绘制线条
if (_points.Length < 2)
return;
// 计算线路总长度
float totalLength = _points.CalculateTotalLength();
float currentLength = 0f;
// 遍历所有点,创建线段并添加到网格中
for (int i = 0; i < _points.Length-1; i++)
{
// 使用静态工具方法创建当前点与下一个点之间的线段
// 传递当前累积长度和总长度用于UV坐标计算
vh.CreateLineSegment_CFAL(_points[i], _points[i+1],
i > 0 ? Mathf.Acos(Vector2.Dot((_points[i] - _points[i -1]).normalized, (_points[i + 1] - _points[i]).normalized)) * Mathf.Rad2Deg : 0,
i < _points.Length-2 ? Mathf.Acos(Vector2.Dot((_points[i] - _points[i + 1]).normalized, (_points[i + 1] - _points[i +2]).normalized)) * Mathf.Rad2Deg : 0,
thickness, center, rectTransform.sizeDelta, color, currentLength, totalLength);
// 更新累积长度
currentLength += Vector2.Distance(_points[i], _points[i+1]);
// 计算当前线段的顶点索引(每个线段包含 5 个顶点)
int index = i * 5;
// 添加线段的两个三角形(构成矩形线段)
vh.AddTriangle(index, index+1, index+3);
vh.AddTriangle(index+3, index+2, index);
// 为线段之间添加斜角边缘
// 使用上一个线段的终点和当前线段的起点创建过渡三角形
if (i != 0)
{
vh.AddTriangle(index, index-1, index-3);
vh.AddTriangle(index+1, index-1, index-2);
}
}
}
}
}