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

223 lines
6.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using UnityEngine;
using System.Collections.Generic;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace GameWrapper.Curve
{
/// <summary>
/// B样条曲线
/// </summary>
public sealed class BSplineCurve : MonoBehaviour
{
[Header("是否本地坐标")]
public bool isLocalPos = true;
[Header("点位索引")]
public int Index;
[Header("下一个目标点位")]
public BSplineCurve NextTarget;
[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();
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;
}
}
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
}
}