Creation of Unity Bezier curve
Effect
Bezier curve principle
The commonly used Bezier curve is a mathematical curve for two-dimensional graphics applications. Curve definition: start point, end point, control point. By adjusting the control points, the shape of the Bezier curve changes.
first order straight line
How to add CC in the image belowPoint C is within the set time, fromAAPoint A moves toBBPoint B ? Here set a timet , t ∈ ( 0 , 1 ) t ,t \in(0,1)t,t∈(0,1),
set ttOA ⃗ \vec{OA}in time tOAEqually shortened to OE ⃗ \vec{OE}OE, OB ⃗ \vec{OB}OBThe unit vector of extends to OF ⃗ \vec{OF}OF。
OE ⃗ = OA ⃗ ( 1 − t ) \vec{OE}=\vec{OA}(1-t)OE=OA(1−t)
O F ⃗ = O B ⃗ × t \vec{OF}=\vec{OB}\times t OF=OB×t
O C ⃗ = O A ⃗ ( 1 − t ) + O B ⃗ × t \vec{OC}=\vec{OA}(1-t)+\vec{OB}\times t OC=OA(1−t)+OB×t
This way you can get CCPoint C is within the set time, fromAAPoint A moves toBBInterpolation of point B.
Vector3 AB = (1 - t) * OA + t * OB;
second order curve
The second-order is based on the first-order algorithm, which is to deal with AB ⃗ \vec{AB} separatelyAB和 B C ⃗ \vec{BC} BC
Vector3 AB = (1 - t) * OA + t * OB;
Vector3 BC = (1 - t) * OB + t * OC;
Then AB ⃗ \vec{AB}AB和 B C ⃗ \vec{BC} BCAlso perform first-order algorithm processing
Vector3 ABC = (1 - t) * AB + t * BC;
third order curve
The third-order is similar to the second-order, and it continues to accumulate.
Vector3 AB = (1 - t) * OA + t * OB;
Vector3 BC = (1 - t) * OB + t * OC;
Vector3 CD = (1 - t) * OC + t * OD;
Vector3 ABC = (1 - t) * AB + t * BC;
Vector3 BCD = (1 - t) * BC + t * CD;
result = (1 - t) * ABC + t * BCD;
The next four high-level and so on. The third order is commonly used in daily life, that is, four points determine a curve.
Application in unity
The purpose is to obtain the points on the curve through the Bezier curve in order to establish the motion path data.
Create a Bezier tool through the editing extension tool of the interface.
#if UNITY_EDITOR
public class BesselPathCreat : ScriptableWizard
{
public string Name = "Path";
// 点的半径
public float radius = 1;
// 曲线取点的密度
public int densityCurve = 1;
/// <summary>
/// 绘制曲线控制点 -- 此脚本的子物体
/// </summary>
public List<GameObject> PathPointList = new List<GameObject>();
DrawGizmosLine drawGizmosLint;
GameObject game;
[MenuItem("FrameWorkSong//FrameWork/3.创建贝塞尔路径", false, 3)]
static void CreateBasselPath()
{
ScriptableWizard.DisplayWizard<BesselPathCreat>("CreateBasselPath", "创建", "加点");
}
/// <summary>
/// 创建按钮触发
/// </summary>
void OnWizardCreate()
{
if (PathPointList.Count > 4)
{
var level = ScriptableObject.CreateInstance<BasselPathTemplet>();
level.BasselPathPoints = drawGizmosLint.CurvePoints;
AssetDatabase.CreateAsset(level, @"Assets/FrameWorkSong/Data/" + Name + ".asset");//在传入的路径中创建资源
AssetDatabase.SaveAssets(); //存储资源
AssetDatabase.Refresh(); //刷新
}
DestroyObject();
}
/// <summary>
/// 关闭触发
/// </summary>
void DestroyObject()
{
if (game)
{
DestroyImmediate(game);
}
for (int i = 0; i < PathPointList.Count; i++)
{
DestroyImmediate(PathPointList[i]);
}
}
void OnWizardUpdate()
{
}
/// <summary>
/// 加点按钮触发
/// </summary>
// When the user presses the "Apply" button OnWizardOtherButton is called.
void OnWizardOtherButton()
{
if (PathPointList.Count == 0)
{
game = new GameObject();
drawGizmosLint = game.AddComponent<DrawGizmosLine>();
}
BasselPath basselPath = new BasselPath();
DrawGizmosPointLine drawGizmos = basselPath.MianPiont.AddComponent<DrawGizmosPointLine>();
PathPointList.Add(basselPath.FrontPiont);
PathPointList.Add(basselPath.MianPiont);
PathPointList.Add(basselPath.BackePiont);
drawGizmos.SelfPoint = basselPath.Piont;
drawGizmosLint.SelfPoint = PathPointList;
drawGizmosLint.densityCurve = densityCurve;
drawGizmosLint.radius = radius;
}
void OnDestroy()
{
DestroyObject();
}
}
#endif
Used here, the class is derived to create editor wizards. For details, please refer to the official api https://docs.unity3d.com/cn/current/ScriptReference/ScriptableWizard.html
How to create Bezier
public class BasselPath
{
List<GameObject> piont = new List<GameObject>();
GameObject mianPiont;
GameObject frontPiont;
GameObject backePiont;
public BasselPath()
{
mianPiont = new GameObject();
mianPiont.transform.position = Vector3.zero;
frontPiont = new GameObject();
frontPiont.transform.position = Vector3.zero + (Vector3.right * 5);
backePiont = new GameObject();
backePiont.transform.position = Vector3.zero + (Vector3.left * 5);
piont.Add(frontPiont);
piont.Add(mianPiont);
piont.Add(backePiont);
backePiont.transform.SetParent(mianPiont.transform);
frontPiont.transform.SetParent(mianPiont.transform);
mianPiont.AddComponent<DrawGizmosPoint>();
frontPiont.AddComponent<DrawGizmosPoint>();
backePiont.AddComponent<DrawGizmosPoint>();
}
public GameObject MianPiont { get => mianPiont; set => mianPiont = value; }
public GameObject FrontPiont { get => frontPiont; }
public GameObject BackePiont { get => backePiont; }
public List<GameObject> Piont { get => piont; }
}
/// <summary>
/// 绘制节点
/// </summary>
public class DrawGizmosPoint : MonoBehaviour
{
public float radius = 1;
private void OnDrawGizmos()
{
//绘制点
Gizmos.color = Color.blue;
Gizmos.DrawSphere(transform.position, radius * 0.5f);
}
}
/// <summary>
/// 绘制节点线
/// </summary>
public class DrawGizmosPointLine : MonoBehaviour
{
public float radius = 1;
public List<GameObject> SelfPoint = new List<GameObject>();
private void OnDrawGizmos()
{
List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();
//绘制曲线控制点连线
Gizmos.color = Color.red;
for (int i = 0; i < controlPointPos.Count - 1; i += 3)
{
Gizmos.DrawLine(controlPointPos[i], controlPointPos[i + 1]);
Gizmos.DrawLine(controlPointPos[i + 1], controlPointPos[i + 2]);
}
}
}
/// <summary>
/// 绘制曲线
/// </summary>
public class DrawGizmosLine : MonoBehaviour
{
public float radius;
public int densityCurve;
public List<GameObject> SelfPoint;
public List<Vector3> CurvePoints;
private void OnDrawGizmos()
{
List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();
if (controlPointPos != null)
{
CurvePoints = GetDrawingPoints(controlPointPos, densityCurve);
}
//绘制曲线
if (CurvePoints.Count >= 4)
{
Gizmos.color = Color.green;
//点密度
foreach (var item in CurvePoints)
{
Gizmos.DrawSphere(item, radius * 0.5f);
}
//曲线
for (int i = 0; i < CurvePoints.Count - 1; i++)
{
Gizmos.DrawLine(CurvePoints[i], CurvePoints[i + 1]);
}
}
}
/// <summary>
/// 获取绘制点
/// </summary>
/// <param name="controlPoints"></param>
/// <param name="segmentsPerCurve"></param>
/// <returns></returns>
public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
{
List<Vector3> points = new List<Vector3>();
// 下一段的起始点和上段终点是一个,所以是 i+=3
for (int i = 1; i < controlPoints.Count - 4; i += 3)
{
var p0 = controlPoints[i];
var p1 = controlPoints[i + 1];
var p2 = controlPoints[i + 2];
var p3 = controlPoints[i + 3];
float dis = Vector3.Distance(p0, p3);
int count = Mathf.CeilToInt(segmentsPerCurve * dis);
if (count < segmentsPerCurve)
{
count = segmentsPerCurve;
}
for (int j = 0; j <= count; j++)
{
var t = j / (float)count;
points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
}
}
return points;
}
// 三阶公式
Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 result;
Vector3 p0p1 = (1 - t) * p0 + t * p1;
Vector3 p1p2 = (1 - t) * p1 + t * p2;
Vector3 p2p3 = (1 - t) * p2 + t * p3;
Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
result = (1 - t) * p0p1p2 + t * p1p2p3;
return result;
}
}