unity 控制点 贝塞尔曲线_Unity曲线编辑器和bezier曲线插值

还有一个类似功能的插件SWS

曲线编辑,可用于不规则路线,N个2阶bezier 替代高阶,达到曲线插值 的额性能和速度平衡

为了曲线公衡平滑可以用N个3阶的特性调节细节

BezierMgrEditor.cs

using UnityEngine;

using UnityEditor;

using System;

using System.Collections;

using System.Collections.Generic;

[CustomEditor(typeof(BezierMgr))]

public class BezierMgrEditor : Editor

{

private SerializedObject m_Object;

private SerializedProperty m_ShowWayPointInGame;

private SerializedProperty m_IsTest;

private SerializedProperty m_Id;

//called whenever this inspector window is loaded

public void OnEnable()

{

//we create a reference to our script object by passing in the target

m_Object = new SerializedObject(target);

m_ShowWayPointInGame = m_Object.FindProperty("showWayPoint");

m_IsTest = m_Object.FindProperty("isWayPointTest");

m_Id = m_Object.FindProperty("id");

}

//called whenever the inspector gui gets rendered

public override void OnInspectorGUI()

{

//this pulls the relative variables from unity runtime and stores them in the object

m_Object.Update();

this.RenameAll();

GUILayout.BeginHorizontal();

GUILayout.Label("--------------游戏设置--------------");

GUILayout.EndHorizontal();

this.m_Id.intValue = EditorGUILayout.IntField("该路点id:", m_Id.intValue, GUILayout.Width(300.0f));

this.m_ShowWayPointInGame.boolValue = EditorGUILayout.Toggle("游戏中显示路点", m_ShowWayPointInGame.boolValue, GUILayout.Width(300.0f));

this.m_IsTest.boolValue = EditorGUILayout.Toggle("开启路点测试", m_IsTest.boolValue, GUILayout.Width(300.0f));

//waypoint index header

GUILayout.Label("Bezier Points: ", EditorStyles.boldLabel);

GUILayout.BeginHorizontal();

if (GUILayout.Button("add"))

{

AddPoint();

}

if (GUILayout.Button("remove"))

{

RemovePoint();

}

if (GUILayout.Button("remove all"))

{

this.RemoveAllPoints();

}

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

GUILayout.Label("---------------------------------");

GUILayout.EndHorizontal();

if (points.Count % 3 != 0)

{

GUILayout.BeginHorizontal();

GUILayout.Label("Error:当前路点不为3的整数倍");

GUILayout.EndHorizontal();

}

GUILayout.BeginHorizontal();

GUILayout.Label("----------信---息----------");

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

GUILayout.Label("当前二阶Bezier曲线数量:" + points.Count / 3);

GUILayout.EndHorizontal();

for (int i = 0; i < 3; i++)

{

GUILayout.BeginHorizontal();

//indicate each array slot with index number in front of it

//create an object field for every waypoint

// EditorGUILayout.ObjectField(waypoints[i], typeof(Transform), true);

GUILayout.EndHorizontal();

}

//we push our modified variables back to our serialized object

m_Object.ApplyModifiedProperties();

}

ArrayList points = new ArrayList();

public void AddPoint()

{

this.RenameAll();

int count = 3 - points.Count % 3;

for (int j = 0; j < count; j++)

{

GameObject p = GameObject.Instantiate(GameObject.Find("__BezierTemplatePoint"));

if (points.Count > 0)

{

p.transform.position = (points[points.Count - 1] as GameObject).transform.position + Vector3.left * 10.0f;

}

points.Add(p);

p.name = "BezierPoint " + (points.Count);

p.transform.SetParent((m_Object.targetObject as BezierMgr).gameObject.transform);

// p.transform.SetSiblingIndex(1);

this.RenameAll();

}

}

void SyncAll()

{

this.RenameAll();

}

private void RenameAll()

{

//先删除不在记录中的点

var objj = new SerializedObject(target);

var parent = (objj.targetObject as BezierMgr).gameObject;

var pp = parent.GetComponentsInChildren();

ArrayList real = new ArrayList();

for (int i = 1; i < pp.Length; i++)

{

Transform tr = pp[i] as Transform;

real.Add(tr.gameObject);

}

// rename all

int idx = 0;

bool swap = (real.Count) != points.Count && points.Count != 0; //只有不足3的倍数时 才交换,因为可能会删除某个点来作为

points.Clear();

foreach (GameObject obj in real)

{

points.Add(obj);

string point_name_tag = "";

const string Name_Tag_Head = "_Head";

const string Name_Tag_Mid = "_Mid";

const string Name_Tag_End = "_End";

if (idx % 3 == 0)

{

point_name_tag = Name_Tag_Head;

}

else if (idx % 3 == 1)

{

point_name_tag = Name_Tag_Mid;

}

else

{

point_name_tag = Name_Tag_End;

}

string newname = "BezierPoint Rename " + idx;

Transform oldObj = parent.transform.Find(newname + Name_Tag_Head);

if (oldObj == null)

{

oldObj = parent.transform.Find(newname + Name_Tag_Mid);

}

if (oldObj == null)

{

oldObj = parent.transform.Find(newname + Name_Tag_End);

}

if (oldObj != null && swap)

{//replace new position to old position avoid miss info

obj.transform.position = oldObj.position;

}

obj.name = newname + point_name_tag;

++idx;

}

}

bool HasObject(GameObject obj)

{

foreach (GameObject ob in points)

{

if (ob == obj) return true;

}

return false;

}

public void RemovePoint()

{

this.RenameAll();

if (points.Count <= 0) return;

int offset = 1;

if (points.Count % 3 == 0) offset = 3;

for (int i = 0; i < offset; i++)

{

GameObject p = points[points.Count - 1] as GameObject;

GameObject.DestroyImmediate(p);

points.Remove(p);

}

this.RenameAll();

}

public void RemoveAllPoints()

{

var parent = (this.m_Object.targetObject as BezierMgr).gameObject;

var pp = parent.GetComponentsInChildren();

ArrayList real = new ArrayList();

for (int i = 1; i < pp.Length; i++)

{

Transform tr = pp[i] as Transform;

GameObject.DestroyImmediate(tr.gameObject);

}

points.Clear();

}

}

BezierMgr.cs

/*

* Author: caoshanshan

* Email: [email protected]

*/

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

// 2次 贝塞尔 曲线管理器,用于拼接多个低阶 组合成平滑路径,

public class BezierMgr : MonoBehaviour

{

[Header("生成可显示路点否")]

[SerializeField]

public bool showWayPoint = true;

[Header("显示路点的精度")]

[SerializeField]

public float tolerance = 0.001f;

[Header("显示路点的缩放")]

[SerializeField]

public float scale = 0.1f;

[Header("开启路点测试后,汽车前进速度不会变化,只是测试路径平滑度使用")]

[SerializeField]

public bool isWayPointTest = true;

public int id = 0;//改路点id 一个赛道允许多个路点,用id表示

[HideInInspector]

public static BezierMgr ins;

public BezierMgr()

{

ins = this;

}

private int CURRENT_INDEX = 0;

public Bezier3 GetNextBezier()

{

if (bezier_queue.Count <= 0)

{

this.Awake();

}

if (CURRENT_INDEX >= bezier_queue.Count)

{

CURRENT_INDEX = 0; // for loop

}

int index = CURRENT_INDEX;

CURRENT_INDEX++;

var b = bezier_queue[index] as Bezier3;

return b;

}

// n阶段 bezier points

public static List GetBezierPoints(List pathToCurve, int interpolations)

{

List tempPoints;

List curvedPoints;

int pointsLength = 0;

int curvedLength = 0;

if (interpolations < 1)

interpolations = 1;

pointsLength = pathToCurve.Count;

curvedLength = (pointsLength * Mathf.RoundToInt(interpolations)) - 1;

curvedPoints = new List(curvedLength);

float t = 0.0f;

for (int pointInTimeOnCurve = 0; pointInTimeOnCurve < curvedLength + 1; pointInTimeOnCurve++)

{

t = Mathf.InverseLerp(0, curvedLength, pointInTimeOnCurve);

tempPoints = new List(pathToCurve);

for (int j = pointsLength - 1; j > 0; j--)

{

for (int i = 0; i < j; i++)

{

tempPoints[i] = (1 - t) * tempPoints[i] + t * tempPoints[i + 1];

}

}

curvedPoints.Add(tempPoints[0]);

}

return curvedPoints;

}

public void Init()

{

bezier_queue.Clear();

foreach (GameObject obj in show_queue)

{

GameObject.DestroyImmediate(obj);

}

show_queue.Clear();

CURRENT_INDEX = 0;

}

ArrayList show_queue = new ArrayList();

ArrayList bezier_queue = new ArrayList();

void Awake()

{

this.Init();

this.bezier_queue = this.GetBezierQueue();

}

bool hasShow = false;

ArrayList GetBezierQueue(bool editorMode = false)

{

var points = this.GetComponentsInChildren();

ArrayList ret = new ArrayList();

ArrayList bezier_points = new ArrayList();

int i = 0;

foreach (Transform point in points)

{

if (i != 0 && editorMode == false)

{

// point.gameObject.SetActive(false);

}

bezier_points.Add(point.position);

i++;

}

i--;

bezier_points.RemoveAt(0);

if (i % 3 != 0 && editorMode == false)

{

Debug.LogError("error of number of way point " + bezier_points.Count);

}

for (int ii = 0; ii < bezier_points.Count; ii += 3)

{

//根据路点 生成 2阶bezier 集合

Vector3 point = (Vector3)bezier_points[ii];

var bezier = new Bezier3();

bezier.id = ii / 3;

if (editorMode)

{

try

{

bezier.p0 = (Vector3)bezier_points[ii];

bezier.p1 = (Vector3)bezier_points[ii + 1];

bezier.p2 = (Vector3)bezier_points[ii + 2];

ret.Add(bezier);

}

catch (System.Exception e)

{

}

}

else

{

bezier.p0 = (Vector3)bezier_points[ii];

bezier.p1 = (Vector3)bezier_points[ii + 1];

bezier.p2 = (Vector3)bezier_points[ii + 2];

ret.Add(bezier);

}

}

if (editorMode == false && showWayPoint && hasShow == false)

{

this.bezier_queue = ret;

GameObject cube = GameObject.Find("__WayPointShowCube");

GameObject p = GameObject.Find("__DrawShowPoints");

if (Application.isPlaying)

{

hasShow = true;

foreach (Bezier3 be in bezier_queue)

{

Vector3 p0 = be.p0;

Vector3 p1 = be.p1;

Vector3 p2 = be.p2;

for (float time = 0f; time < 1.0f; time += tolerance)

{

float t = time;

Vector3 pos = be.GetPoint(t);

var obj = GameObject.Instantiate(cube, p.transform);

obj.transform.position = pos;

obj.name = "clone";

obj.transform.localScale = new Vector3(scale, scale, scale);

show_queue.Add(obj);

}

}

}

}

return ret;

}

void Start()

{

}

void Update()

{

}

void OnDrawGizmos()

{

ArrayList bezier_queue = this.GetBezierQueue(true); ;

ArrayList pointss = new ArrayList();

foreach (Bezier3 be in bezier_queue)

{

for (float time = 0f; time < 1.0f; time += 0.001f)

{

float t = time;

Vector3 pos = be.GetPoint(t);

pointss.Add(pos);

}

}

for (int ii = 0; ii < pointss.Count - 3; ii += 2)

{

Gizmos.DrawLine((Vector3)(pointss[ii]), (Vector3)(pointss[ii + 1]));

}

}

}

Bezier3.cs

/*

* Author: caoshanshan

* Email: [email protected]

*/

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

//2次贝塞尔曲线

//为了计算性能和精度 暂不用高阶生成,用多个2阶 实时计算 拼接出完整赛道

// 额外的工作是 每个2阶的粘合处平稳过渡

public class Bezier3

{

public Vector3 GetPoint(float t)

{

Vector3 pos = (1 - t) * (1 - t) * p0 + 2 * t * (1 - t) * p1 + t * t * p2;

return pos;

}

public Bezier3()

{

}

public Vector3 p0 = Vector3.zero;

public Vector3 p1 = Vector3.zero;

public Vector3 p2 = Vector3.zero;

public int id = 0;//id

}

你可能感兴趣的:(unity,控制点,贝塞尔曲线)