udpate 0.6.3.3

This commit is contained in:
2026-07-20 11:08:12 +08:00
parent acaa1cd57a
commit 603f5a24c2
71 changed files with 57665 additions and 2183 deletions
@@ -0,0 +1,245 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// 曲线缓存渲染器基类
/// 管理 CurveCacheEntry 列表 + 扁平控制点/宽度/箭头缓存 + 脏标记增量重建
/// LateUpdate 中只重建脏标记范围内的曲线
/// </summary>
[RequireComponent(typeof(CanvasRenderer))]
public abstract class CurveCacheRendererBase : MaskableGraphic
{
#region 默认材质
private static Material s_DefaultMaterial;
private const string DefaultShaderPath = "XericLibrary/UIGraph/XericUICurve";
public override Material defaultMaterial
{
get
{
if (s_DefaultMaterial == null)
{
var shader = Shader.Find(DefaultShaderPath);
if (shader != null)
s_DefaultMaterial = new Material(shader);
else
s_DefaultMaterial = base.defaultMaterial;
}
return s_DefaultMaterial;
}
}
#endregion
#region 数据缓存
/// <summary>曲线条目列表</summary>
protected List<CurveCacheEntry> m_Curves = new List<CurveCacheEntry>();
/// <summary>所有曲线的控制点扁平存储</summary>
protected List<Vector2> m_ControlPoints = new List<Vector2>();
/// <summary>所有曲线的宽度扁平存储(与控制点一一对应)</summary>
protected List<float> m_Widths = new List<float>();
/// <summary>所有曲线的箭头扁平存储</summary>
protected List<ArrowHeadData> m_Arrows = new List<ArrowHeadData>();
/// <summary>模型顶点缓存</summary>
protected List<UIVertex> m_MeshVertexCache = new List<UIVertex>();
/// <summary>三角形索引缓存</summary>
protected List<int> m_MeshIndexCache = new List<int>();
/// <summary>脏标记范围起点(曲线索引),-1 表示无脏标记</summary>
protected int m_DirtyStartIndex = -1;
/// <summary>脏标记范围终点(曲线索引)</summary>
protected int m_DirtyEndIndex = -1;
#endregion
#region API
/// <summary>
/// 添加一条曲线
/// </summary>
/// <param name="entry">曲线缓存条目(控制点计数和起始索引需预先设置)</param>
/// <param name="controlPoints">控制点数组</param>
/// <param name="widths">宽度数组(长度需与控制点一致)</param>
/// <param name="arrows">箭头数组(可选)</param>
/// <returns>曲线索引</returns>
public int AddCurve(CurveCacheEntry entry, Vector2[] controlPoints, float[] widths, ArrowHeadData[] arrows = null)
{
int index = m_Curves.Count;
// 填充起始索引
entry.controlPointStartIndex = m_ControlPoints.Count;
entry.widthStartIndex = m_Widths.Count;
entry.arrowStartIndex = (arrows != null && arrows.Length > 0) ? m_Arrows.Count : -1;
entry.arrowCount = arrows?.Length ?? 0;
entry.controlPointCount = controlPoints.Length;
m_Curves.Add(entry);
m_ControlPoints.AddRange(controlPoints);
m_Widths.AddRange(widths);
if (arrows != null)
m_Arrows.AddRange(arrows);
MarkDirty(index);
return index;
}
/// <summary>
/// 移除指定曲线
/// </summary>
public void RemoveCurve(int index)
{
if (index < 0 || index >= m_Curves.Count) return;
m_Curves.RemoveAt(index);
RebuildAll();
}
/// <summary>
/// 标记指定曲线为脏
/// </summary>
public void SetCurveDirty(int index)
{
MarkDirty(index);
}
/// <summary>
/// 清空所有曲线
/// </summary>
public void ClearAll()
{
m_Curves.Clear();
m_ControlPoints.Clear();
m_Widths.Clear();
m_Arrows.Clear();
m_MeshVertexCache.Clear();
m_MeshIndexCache.Clear();
m_DirtyStartIndex = -1;
m_DirtyEndIndex = -1;
SetVerticesDirty();
}
#endregion
#region 脏标记
protected void MarkDirty(int index)
{
if (m_DirtyStartIndex < 0 || index < m_DirtyStartIndex)
m_DirtyStartIndex = index;
if (m_DirtyEndIndex < 0 || index > m_DirtyEndIndex)
m_DirtyEndIndex = index;
}
/// <summary>
/// 标记所有曲线为脏(下次 LateUpdate 全量重绘)
/// 适用于:更新控制点/宽度后,不改变元素数量的重绘
/// </summary>
public void RebuildAll()
{
m_DirtyStartIndex = 0;
m_DirtyEndIndex = m_Curves.Count - 1;
if (m_DirtyEndIndex < 0)
m_DirtyStartIndex = -1;
}
#endregion
#region LateUpdate
protected virtual void LateUpdate()
{
if (m_DirtyStartIndex < 0) return;
int start = m_DirtyStartIndex;
// 统计脏范围前的顶点/索引数(这些无需重建)
int preVertexCount = 0;
int preIndexCount = 0;
for (int i = 0; i < start; i++)
{
preVertexCount += m_Curves[i].vertexCount;
preIndexCount += m_Curves[i].triangleCount * 3;
}
// 从 start 开始重建所有曲线
var tempVerts = new List<UIVertex>();
var tempIndices = new List<int>();
int vAccum = preVertexCount;
int iAccum = preIndexCount;
for (int i = start; i < m_Curves.Count; i++)
{
var entry = m_Curves[i];
int vertBefore = tempVerts.Count;
int idxBefore = tempIndices.Count;
CurveMeshGenerator.GenerateCurve(
entry, m_ControlPoints, m_Widths, m_Arrows,
tempVerts, tempIndices);
var updated = entry;
updated.startVertexIndex = vAccum;
updated.vertexCount = tempVerts.Count - vertBefore;
updated.startIndexIndex = iAccum;
updated.triangleCount = (tempIndices.Count - idxBefore) / 3;
m_Curves[i] = updated;
vAccum += updated.vertexCount;
iAccum += updated.triangleCount * 3;
}
// 合并到主缓存
int totalVertices = vAccum;
int totalIndices = iAccum;
while (m_MeshVertexCache.Count < totalVertices)
m_MeshVertexCache.Add(new UIVertex());
while (m_MeshIndexCache.Count < totalIndices)
m_MeshIndexCache.Add(0);
for (int i = 0; i < tempVerts.Count; i++)
m_MeshVertexCache[preVertexCount + i] = tempVerts[i];
for (int i = 0; i < tempIndices.Count; i++)
m_MeshIndexCache[preIndexCount + i] = tempIndices[i] + preVertexCount;
SetVerticesDirty();
m_DirtyStartIndex = -1;
m_DirtyEndIndex = -1;
}
#endregion
#region OnPopulateMesh
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if (m_MeshVertexCache.Count == 0 || m_MeshIndexCache.Count == 0) return;
vh.AddUIVertexStream(m_MeshVertexCache, m_MeshIndexCache);
}
#endregion
#region 生命周期
protected override void OnEnable()
{
base.OnEnable();
RebuildAll();
}
protected override void OnDestroy()
{
base.OnDestroy();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 905c29aca6f80f64f8da8cf8579868ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,333 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using XericLibrary.Runtime.MacroLibrary;
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// 曲线网格生成器
/// 将贝塞尔曲线细分为线段带状三角片 + 生成箭头装饰
///
/// 核心算法:
/// 1. 在 t∈[0,1] 上均匀采样 N+1 个点
/// 2. 每个采样点计算位置、切线、宽度、颜色
/// 3. 切线转 90° 得切法线,沿切法线位移 halfWidth 得左右边顶点
/// 4. 顺序连接左右边顶点形成 ribbon 带状:
/// 左[i]──右[i]──左[i+1]──右[i+1]──...
/// 每个四边形拆两个三角形
/// </summary>
public static class CurveMeshGenerator
{
/// <summary>圆圈箭头多边形边数</summary>
private const int CircleArrowSides = 8;
/// <summary>计算切线时的 epsilon 采样偏移</summary>
private const float TangentEpsilon = 0.001f;
/// <summary>
/// 生成单条曲线的网格数据
/// </summary>
public static void GenerateCurve(
in CurveCacheEntry entry,
List<Vector2> controlPointsPool,
List<float> widthsPool,
List<ArrowHeadData> arrowsPool,
List<UIVertex> vertexList,
List<int> indexList)
{
int segCount = entry.tessellationSegments;
if (segCount < 1) segCount = 4;
int sampleCount = segCount + 1; // N+1 个采样点
int cpStart = entry.controlPointStartIndex;
int cpCount = entry.controlPointCount;
int wStart = entry.widthStartIndex;
var ctrlPts = new Vector2[cpCount];
var wPts = new float[cpCount];
for (int i = 0; i < cpCount; i++)
{
ctrlPts[i] = controlPointsPool[cpStart + i];
wPts[i] = widthsPool[wStart + i];
}
int baseVertex = vertexList.Count;
UIVertex vert = new UIVertex
{
normal = Vector3.back,
tangent = new Vector4(1f, 0f, 0f, -1f),
uv1 = new Vector2(1f, 0f),
};
// 1. 采样 + 生成左右边顶点
for (int s = 0; s < sampleCount; s++)
{
float t = (float)s / segCount;
Vector2 pos = MacroCurve.BezierCurve(ctrlPts, t); // 曲线上点
float width = MacroCurve.BezierCurve(wPts, t); // 当前宽度
Vector2 tangent = ComputeTangent(ctrlPts, t); // 切线方向
Vector2 normal = new Vector2(-tangent.y, tangent.x); // 切法线(左转90°)
MacroCurve.BezierCurve1(entry.startColor, entry.endColor, t, out Color col);
Color32 color = col;
float halfW = width * 0.5f;
float uv = (float)s / segCount;
// 左边顶点
vert.position = pos - normal * halfW;
vert.color = color;
vert.uv0 = new Vector2(0f, uv);
vertexList.Add(vert);
// 右边顶点
vert.position = pos + normal * halfW;
vert.color = color;
vert.uv0 = new Vector2(1f, uv);
vertexList.Add(vert);
}
// 2. 连接带状三角形
// 左[i]=v[i*2], 右[i]=v[i*2+1]
// 四边形 i→i+1: (左i, 右i, 左i+1) + (右i, 右i+1, 左i+1)
for (int s = 0; s < segCount; s++)
{
int bv = baseVertex + s * 2;
indexList.Add(bv + 0); // 左i
indexList.Add(bv + 1); // 右i
indexList.Add(bv + 2); // 左i+1
indexList.Add(bv + 1); // 右i
indexList.Add(bv + 3); // 右i+1
indexList.Add(bv + 2); // 左i+1
}
// 3. 生成箭头
if (entry.arrowCount > 0 && entry.arrowStartIndex >= 0)
{
for (int a = 0; a < entry.arrowCount; a++)
{
var arrow = arrowsPool[entry.arrowStartIndex + a];
GenerateArrow(arrow, ctrlPts, vertexList, indexList);
}
}
}
/// <summary>
/// 计算曲线上 t 处的切线方向(差分法)
/// </summary>
private static Vector2 ComputeTangent(Vector2[] ctrlPts, float t)
{
float t0 = Mathf.Max(0f, t - TangentEpsilon);
float t1 = Mathf.Min(1f, t + TangentEpsilon);
Vector2 tangent = MacroCurve.BezierCurve(ctrlPts, t1) - MacroCurve.BezierCurve(ctrlPts, t0);
if (tangent.sqrMagnitude < float.Epsilon)
tangent = Vector2.up;
else
tangent.Normalize();
return tangent;
}
#region 箭头
private static void GenerateArrow(
ArrowHeadData arrow,
Vector2[] controlPoints,
List<UIVertex> vertexList,
List<int> indexList)
{
float t = Mathf.Clamp01(arrow.progress);
Vector2 pos = MacroCurve.BezierCurve(controlPoints, t);
Vector2 tangent = ComputeTangent(controlPoints, t);
if (arrow.reversed)
tangent = -tangent;
// 深度补偿:半长偏移(箭头中心在本地x=0,尖端在+width*0.5,尾部在-width*0.5)
pos += tangent * arrow.width * 0.5f * arrow.depthCompensation;
int baseVert = vertexList.Count;
GenerateArrowShape(arrow.shape, pos, tangent, arrow.width, arrow.height, arrow.color,
vertexList, indexList, baseVert, t);
}
private static void GenerateArrowShape(
ArrowShape shape,
Vector2 position,
Vector2 tangent,
float width,
float height,
Color32 color,
List<UIVertex> vertexList,
List<int> indexList,
int baseVertex,
float progressT)
{
float cos = tangent.x;
float sin = tangent.y;
Vector2 TransformPoint(float ux, float uy)
{
float x = ux * width;
float y = uy * height;
float rx = x * cos - y * sin;
float ry = x * sin + y * cos;
return position + new Vector2(rx, ry);
}
UIVertex vert = new UIVertex
{
color = color,
normal = Vector3.back,
tangent = new Vector4(1f, 0f, 0f, -1f),
uv0 = new Vector2(0.5f, progressT),
uv1 = new Vector2(0f, 1f)
};
switch (shape)
{
case ArrowShape.Triangle:
{
vert.position = TransformPoint(0.5f, 0f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f, -0.3f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f, 0.3f);
vertexList.Add(vert);
indexList.Add(baseVertex + 0);
indexList.Add(baseVertex + 1);
indexList.Add(baseVertex + 2);
break;
}
case ArrowShape.HollowTriangle:
{
float tailOff = -0.1f;
// 外三角
vert.position = TransformPoint(0.5f, 0f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f, -0.3f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f, 0.3f);
vertexList.Add(vert);
// 内三角(缩小 + 向尾部偏移)
vert.position = TransformPoint(0.5f * 0.5f + tailOff, 0f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f * 0.5f + tailOff, -0.3f * 0.5f * 0.6f);
vertexList.Add(vert);
vert.position = TransformPoint(-0.5f * 0.5f + tailOff, 0.3f * 0.5f * 0.6f);
vertexList.Add(vert);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 5);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 2);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 4);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 3);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 3); indexList.Add(baseVertex + 5);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 2);
break;
}
case ArrowShape.Arrow:
{
float headBaseU = 0.5f - 0.35f; // 头部基座 x=0.15
float shaftEndU = -0.8f; // 杆尾
vert.position = TransformPoint(0.5f, 0f);
vertexList.Add(vert); // 0: 尖端
vert.position = TransformPoint(headBaseU, -0.25f);
vertexList.Add(vert); // 1: 左翼
vert.position = TransformPoint(headBaseU, 0.25f);
vertexList.Add(vert); // 2: 右翼
vert.position = TransformPoint(headBaseU, -0.06f);
vertexList.Add(vert); // 3: 杆起点下
vert.position = TransformPoint(headBaseU, 0.06f);
vertexList.Add(vert); // 4: 杆起点上
vert.position = TransformPoint(shaftEndU, -0.06f);
vertexList.Add(vert); // 5: 杆尾下
vert.position = TransformPoint(shaftEndU, 0.06f);
vertexList.Add(vert); // 6: 杆尾上
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 6);
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 1);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 5);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 3);
break;
}
case ArrowShape.Circle:
{
float r = 0.4f;
vert.position = TransformPoint(0f, 0f);
vertexList.Add(vert);
for (int i = 0; i < CircleArrowSides; i++)
{
float angle = i * Mathf.PI * 2f / CircleArrowSides;
vert.position = TransformPoint(Mathf.Cos(angle) * r, Mathf.Sin(angle) * r);
vertexList.Add(vert);
}
for (int i = 0; i < CircleArrowSides; i++)
{
int next = (i + 1) % CircleArrowSides;
indexList.Add(baseVertex + 0);
indexList.Add(baseVertex + 1 + i);
indexList.Add(baseVertex + 1 + next);
}
break;
}
case ArrowShape.Square:
{
float hs = 0.35f;
vert.position = TransformPoint(-hs, -hs);
vertexList.Add(vert);
vert.position = TransformPoint(hs, -hs);
vertexList.Add(vert);
vert.position = TransformPoint(hs, hs);
vertexList.Add(vert);
vert.position = TransformPoint(-hs, hs);
vertexList.Add(vert);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 3);
break;
}
case ArrowShape.ReverseArrow:
{
float rHeadBaseU = 0.5f - 0.35f;
float rShaftEndU = -0.8f;
vert.position = TransformPoint(0.5f, 0f);
vertexList.Add(vert);
vert.position = TransformPoint(rHeadBaseU, -0.25f);
vertexList.Add(vert);
vert.position = TransformPoint(rHeadBaseU, 0.25f);
vertexList.Add(vert);
vert.position = TransformPoint(rHeadBaseU, -0.06f);
vertexList.Add(vert);
vert.position = TransformPoint(rHeadBaseU, 0.06f);
vertexList.Add(vert);
vert.position = TransformPoint(rShaftEndU, -0.06f);
vertexList.Add(vert);
vert.position = TransformPoint(rShaftEndU, 0.06f);
vertexList.Add(vert);
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 6);
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 1);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 5);
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 3);
break;
}
}
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b0e0346140ad4847a65df78853dd3dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,86 @@
using System;
using UnityEngine;
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// 箭头形状枚举
/// </summary>
public enum ArrowShape : byte
{
/// <summary>实心三角箭头</summary>
Triangle,
/// <summary>空心三角箭头</summary>
HollowTriangle,
/// <summary>箭矢箭头(翼+杆)</summary>
Arrow,
/// <summary>圆形头</summary>
Circle,
/// <summary>方形头</summary>
Square,
/// <summary>反向箭头(箭尾)</summary>
ReverseArrow,
}
/// <summary>
/// 箭头装饰数据
/// </summary>
[Serializable]
public struct ArrowHeadData
{
/// <summary>箭头形状</summary>
public ArrowShape shape;
/// <summary>是否反向</summary>
public bool reversed;
/// <summary>箭头出现在曲线上的位置 (0~1)</summary>
[Range(0f, 1f)]
public float progress;
/// <summary>箭头宽度(沿切线方向)</summary>
public float width;
/// <summary>箭头高度(垂直切线方向)</summary>
public float height;
/// <summary>深度补偿:-1~1,箭头沿切线偏移 width*0.5*depth 距离。
/// -1:尾部对准曲线点(头部向前伸)
/// 0:中心对准曲线点
/// +1:头部对准曲线点(尾部向后拉)</summary>
[Range(-1f, 1f)]
public float depthCompensation;
/// <summary>箭头颜色</summary>
public Color32 color;
}
/// <summary>
/// 曲线缓存条目
/// 控制点、宽度、箭头数据统一存放在外部扁平列表中,
/// 本结构体只记录起始索引和计数。
/// </summary>
[Serializable]
public struct CurveCacheEntry
{
/// <summary>在网格顶点缓存中的起始索引(由 Base 类回填)</summary>
public int startVertexIndex;
/// <summary>该曲线占用的顶点数(由 Base 类回填)</summary>
public int vertexCount;
/// <summary>在网格索引缓存中的起始索引(由 Base 类回填)</summary>
public int startIndexIndex;
/// <summary>三角形数量(由 Base 类回填)</summary>
public int triangleCount;
/// <summary>控制点数量(≥4,默认三阶贝塞尔)</summary>
public int controlPointCount;
/// <summary>在统一控制点缓存中的起始索引</summary>
public int controlPointStartIndex;
/// <summary>在统一宽度缓存中的起始索引</summary>
public int widthStartIndex;
/// <summary>起始颜色</summary>
public Color32 startColor;
/// <summary>结束颜色</summary>
public Color32 endColor;
/// <summary>在统一箭头缓存中的起始索引(-1 表示无箭头)</summary>
public int arrowStartIndex;
/// <summary>箭头数量</summary>
public int arrowCount;
/// <summary>曲线细分段数</summary>
public int tessellationSegments;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6ce7575b6b9e6a45bdba82ae35018e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+121
View File
@@ -0,0 +1,121 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// 曲线绘制器组件
/// 支持多阶贝塞尔曲线,可配置控制点、宽度渐变、颜色和箭头装饰
/// </summary>
[AddComponentMenu("Xeric Library/UI/UICurveRenderer", 16)]
public class UICurveRenderer : CurveCacheRendererBase
{
/// <summary>控制点,(x,y)=位置, z=宽度</summary>
public Vector3[] controlPoints = new Vector3[]
{
new Vector3(-150f, 0f, 10f),
new Vector3(-50f, 100f, 10f),
new Vector3(50f, -100f, 10f),
new Vector3(150f, 0f, 10f),
};
public Color32 startColor = Color.white;
public Color32 endColor = Color.white;
[Range(4, 128)]
public int tessellationSegments = 32;
public List<ArrowHeadData> arrows = new List<ArrowHeadData>();
public bool autoRebuild = true;
private CurveCacheEntry? m_CurrentEntry;
protected override void OnEnable()
{
base.OnEnable();
if (autoRebuild) RebuildCurve();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
if (controlPoints == null || controlPoints.Length < 2)
controlPoints = new Vector3[] { new Vector3(0f, 0f, 10f), new Vector3(100f, 0f, 10f) };
if (tessellationSegments < 4) tessellationSegments = 4;
if (autoRebuild && isActiveAndEnabled)
{
EditorApplication.delayCall += () =>
{
if (this != null && isActiveAndEnabled)
RebuildCurve();
};
}
}
#endif
/// <summary>
/// 重建曲线
/// </summary>
public void RebuildCurve()
{
ClearAll();
if (controlPoints == null || controlPoints.Length < 2) return;
// 从 Vector3[] 中提取 Vector2[] 位置和 float[] 宽度
int count = controlPoints.Length;
var ctrlPts2 = new Vector2[count];
var widths = new float[count];
for (int i = 0; i < count; i++)
{
ctrlPts2[i] = (Vector2)controlPoints[i];
widths[i] = controlPoints[i].z;
}
var entry = new CurveCacheEntry
{
startColor = startColor,
endColor = endColor,
tessellationSegments = tessellationSegments,
};
AddCurve(entry, ctrlPts2, widths, arrows?.ToArray());
m_CurrentEntry = m_Curves.Count > 0 ? m_Curves[0] : (CurveCacheEntry?)null;
}
/// <summary>
/// 设置控制点(z=宽度)
/// </summary>
public void SetControlPoints(Vector3[] points)
{
controlPoints = points;
RebuildCurve();
}
/// <summary>
/// 添加箭头
/// </summary>
public void AddArrow(ArrowHeadData arrow)
{
if (arrows == null) arrows = new List<ArrowHeadData>();
arrows.Add(arrow);
RebuildCurve();
}
/// <summary>
/// 移除箭头
/// </summary>
public void RemoveArrow(int index)
{
if (arrows == null || index < 0 || index >= arrows.Count) return;
arrows.RemoveAt(index);
RebuildCurve();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3189cafe7a5628048b2243986703e3e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: