前言
上一篇貝塞爾曲線的研究只能滿足一段曲線的生成瞄桨,今天將實(shí)現(xiàn)任意曲線生成(多節(jié)點(diǎn)連續(xù)曲線)。
設(shè)計(jì)原理
指定一段曲線的兩個(gè)端點(diǎn)(p0,p3)以及兩個(gè)控制點(diǎn)(p1坷牛,p2)然后使用三階貝塞爾公式即可生成一段連續(xù)的曲線,將這一段曲線理解成第
n個(gè)節(jié)點(diǎn)到n+1個(gè)節(jié)點(diǎn)之間的三階貝塞爾很澄。
接下來(lái)就是創(chuàng)建N個(gè)節(jié)點(diǎn)了京闰,每一個(gè)節(jié)點(diǎn)和其對(duì)應(yīng)的下一個(gè)節(jié)點(diǎn)為一組曲線颜及,最后一個(gè)節(jié)點(diǎn)不計(jì)算。
如何處理多節(jié)點(diǎn)之間的平滑過渡
每一個(gè)節(jié)點(diǎn)都有且僅有一個(gè)控制點(diǎn)蹂楣,控制點(diǎn)左邊的曲線相當(dāng)于三階貝塞爾中的p2點(diǎn)俏站,而右側(cè)則先計(jì)算其鏡像的坐標(biāo)作為公式中的p1,這樣便可以做到一個(gè)控制點(diǎn)同時(shí)調(diào)節(jié)左右曲線的曲率痊土,效果如下肄扎。
設(shè)計(jì)原理
BezierNodeObject :節(jié)點(diǎn)對(duì)象,包含一個(gè)控制點(diǎn)屬性
public class BezierNodeObject : MonoBehaviour
{
public Transform BezierOffset;
private BezierNode bezierNode;
public BezierNode GetBezierNode()
{
bezierNode.nodeOffset = BezierOffset.position;
bezierNode.nodePos = transform.position;
return bezierNode;
}
private void Update()
{
Debug.DrawLine(BezierOffset.position, transform.position - (BezierOffset.position - transform.position), Color.yellow);
}
}
BezierData : 將所有節(jié)點(diǎn)和控制點(diǎn)數(shù)據(jù)保存赁酝,并提供三階貝塞爾函數(shù)接口
[CreateAssetMenu(fileName = "BezierData", menuName = "Config/BezierData")]
public class BezierData : ScriptableObject
{
[Header("數(shù)據(jù)集名稱")]
public string DataName;
[Header("數(shù)據(jù)節(jié)點(diǎn)集")]
public List<BezierNode> bezierNodes;
[Header("精度系數(shù)犯祠,越大越平滑,性能消耗越高"), Range(10, 100)]
public int accuracy = 10;
/// <summary>
/// 計(jì)算并返回指定一段曲線的坐標(biāo)位置數(shù)組
/// </summary>
/// <param name="region">區(qū)間下標(biāo)數(shù)值</param>
/// <returns></returns>
public Vector3[] GetBezierDatas(int region) {
if (region < bezierNodes.Count) {
Vector3[] datas = new Vector3[accuracy];
for (int i = 0; i < accuracy; i++)
{
BezierMath.Bezier_3ref(
ref datas[i],
bezierNodes[region].nodePos,
bezierNodes[region].getReverseNodeOffset(),
bezierNodes[region + 1].nodeOffset,
bezierNodes[region + 1].nodePos,
i/(accuracy-1.0f)
);
}
return datas;
}
return null;
}
public void SetBezierNode(List<BezierNodeObject> bezierNodeObjects) {
if (bezierNodes == null) bezierNodes = new List<BezierNode>();
bezierNodes.Clear();
foreach (var item in bezierNodeObjects)
{
bezierNodes.Add(item.GetBezierNode());
}
}
}
BezierLine : 按照一定的精度從BezierData中獲取每一段曲線上的坐標(biāo)酌呆,將坐標(biāo)信息傳遞給Unity的LineRenderer并繪制出line
public class BezierDrawLine : MonoBehaviour
{
private List<BezierNodeObject> bezierNodeObjects;
private BezierData bezierData;
private List<Vector3> vector3s;
public Transform NodesRoot;
[Header("精度系數(shù),表示每一段有多少個(gè)節(jié)點(diǎn)"),Range(10 , 100)]
public int accuracy;
private void Start()
{
bezierNodeObjects = new List<BezierNodeObject>();
bezierData = new BezierData();
vector3s = new List<Vector3>();
}
private void Update()
{
if (!NodesRoot) return;
bezierNodeObjects.Clear();
bezierNodeObjects.AddRange(NodesRoot.GetComponentsInChildren<BezierNodeObject>());
bezierData.SetBezierNode(bezierNodeObjects);
bezierData.accuracy = accuracy;
drawline();
}
void drawline() {
for (int i = 0; i < bezierNodeObjects.Count - 1; i++)
{
var lineRenderer = bezierNodeObjects[i].GetComponent<LineRenderer>();
if (lineRenderer == null) lineRenderer = bezierNodeObjects[i].gameObject.AddComponent<LineRenderer>();
lineRenderer.positionCount = accuracy;
lineRenderer.SetPositions(bezierData.GetBezierDatas(i));
}
}
}
以上代碼僅供參考衡载,如果需要,下方將提供git項(xiàng)目地址隙袁。
實(shí)際效果展示
[項(xiàng)目地址:BezierTool]