NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/GameWrapper/BSplineCurve/BSplineCurve.cs

223 lines
6.4 KiB
C#
Raw Normal View History

2025-10-23 19:20:44 +08:00
using UnityEngine;
using System.Collections.Generic;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
2025-10-24 15:45:51 +08:00
namespace GameWrapper.Curve
2025-10-23 19:20:44 +08:00
{
/// <summary>
/// B样条曲线
/// </summary>
public sealed class BSplineCurve : MonoBehaviour
{
[Header("是否本地坐标")]
2025-10-23 19:53:12 +08:00
public bool isLocalPos = true;
2025-10-23 19:20:44 +08:00
2025-10-24 15:45:51 +08:00
[Header("点位索引")]
public int Index;
[Header("下一个目标点位")]
public BSplineCurve NextTarget;
2025-10-23 19:20:44 +08:00
[Header("控制点设置")]
public List<Vector3> listControlPoint = new();
/// <summary>
/// 实际点位
/// </summary>
private List<Vector3> curvePoints = new();
[Range(2, 7)]
private int degree = 2; // 曲线阶数(次数+1
[Range(0.001f, 0.1f)]
private float stepSize = 0.01f;
#region API
/// <summary>
/// 获取曲线上特定参数值的点
/// </summary>
public Vector3 GetPointOnCurve(float t)
{
if (listControlPoint.Count <= degree || curvePoints.Count == 0)
return Vector3.zero;
int n = listControlPoint.Count - 1;
float normalizedT = Mathf.Lerp(degree, n + 1, Mathf.Clamp01(t));
Vector3 point = Vector3.zero;
float[] knots = GenerateKnotVector();
for (int i = 0; i <= n; i++)
{
float basis = BasisFunction(i, degree, normalizedT, knots);
point += GetPos(i) * basis;
}
return point;
}
/// <summary>
/// 生成B样条曲线
/// </summary>
public void GenerateCurve()
{
curvePoints.Clear();
2025-10-24 15:45:51 +08:00
if (null != NextTarget)
{
Vector3 startPos = gameObject.transform.position;
Vector3 endPos = NextTarget.gameObject.transform.position;
if (isLocalPos)
{
startPos -= gameObject.transform.position;
endPos -= gameObject.transform.position;
}
if (listControlPoint.Count <= 0)
{
listControlPoint.Add(startPos);
listControlPoint.Add(endPos);
}
else if (listControlPoint.Count <= 1)
{
listControlPoint[0] = startPos;
listControlPoint.Add(endPos);
}
else
{
listControlPoint[0] = startPos;
listControlPoint[^1] = endPos;
}
}
2025-10-23 19:20:44 +08:00
if (listControlPoint.Count < 2)
return;
int n = listControlPoint.Count - 1; // 控制点索引最大值
int m = n + degree + 1; // 节点向量最大值
// 生成均匀节点向量
float[] knots = new float[m + 1];
for (int i = 0; i <= m; i++)
{
knots[i] = i;
}
curvePoints.Add(GetPos(0));
// 计算曲线点
for (float t = degree; t <= n + 1; t += stepSize)
{
Vector3 point = Vector3.zero;
for (int i = 0; i <= n; i++)
{
float basis = BasisFunction(i, degree, t, knots);
point += GetPos(i) * basis;
}
curvePoints.Add(point);
}
curvePoints.Add(GetPos(listControlPoint.Count - 1));
}
#endregion
/// <summary>
/// B样条基函数计算De Boor递归算法
/// </summary>
private float BasisFunction(int i, int k, float t, float[] knots)
{
if (0 == k)
return (t >= knots[i] && t < knots[i + 1]) ? 1f : 0f;
else
{
float denominator1 = knots[i + k] - knots[i];
float denominator2 = knots[i + k + 1] - knots[i + 1];
float term1 = 0f;
float term2 = 0f;
if (0 != denominator1)
term1 = (t - knots[i]) / denominator1 * BasisFunction(i, k - 1, t, knots);
if (0 != denominator2)
term2 = (knots[i + k + 1] - t) / denominator2 * BasisFunction(i + 1, k - 1, t, knots);
return term1 + term2;
}
}
/// <summary>
/// 生成节点向量
/// </summary>
private float[] GenerateKnotVector()
{
int n = listControlPoint.Count - 1;
int m = n + degree + 1;
float[] knots = new float[m + 1];
for (int i = 0; i <= m; i++)
{
knots[i] = i;
}
return knots;
}
private Vector3 GetPos(int index)
{
if (isLocalPos)
return listControlPoint[index] + gameObject.transform.position;
else
return listControlPoint[index];
}
#if UNITY_EDITOR
[Header("是否显示控制点")]
public bool isShowControlPoints = true;
[Header("是否显示曲线")]
public bool isShowCurve = true;
[Header("控制点颜色")]
public Color controlPointColor = new(1f, 0f, 0f, 0.8f);
[Header("曲线颜色")]
public Color curveColor = Color.blue;
void OnDrawGizmos()
{
if (Selection.activeGameObject != gameObject)
return;
GenerateCurve();
// 绘制曲线
if (isShowCurve && curvePoints.Count > 1)
{
Gizmos.color = curveColor;
for (int i = 0; i < curvePoints.Count - 1; ++i)
{
Gizmos.DrawLine(curvePoints[i], curvePoints[i + 1]);
}
}
// 绘制控制点
if (isShowControlPoints)
{
Gizmos.color = controlPointColor;
for (int i = 0; i < listControlPoint.Count; ++i)
{
Vector3 point = GetPos(i);
Gizmos.DrawSphere(point, 0.2f);
Gizmos.DrawIcon(point, i.ToString());
}
// 绘制控制多边形
for (int i = 0; i < listControlPoint.Count - 1; ++i)
{
Gizmos.DrawLine(GetPos(i), GetPos(i + 1));
}
}
}
#endif
}
}