在unity中曲线的运用是比较广泛的,如:人物沿着指定的移动轨迹,怪物的指定移动路线,汽车的在指定的轨道运动等。
绘制Bezier路线:需要了解一下什么是贝塞尔,同时需要unity 的Gizmos与Handles,我这里就不介绍了。
整体思路:
1. 使用贝塞尔的三阶方程,目的:绘制路线的主要步骤。
2. 在Inspector面板创建一些空物体来表示路径点
3. 在Bezier在不考虑Loop时,需要的点是:4,7,10,13........
4.代码创建辅助点,用来调节路径的弧度。
5.计算段数,第一段是特殊的需要四个点,后面的都是以三个点为一段
6.计算每一段的距离
7.控制点的移动,动态更新点之间的距离
8.增加点
9.删除点
Bezier_B.gif
脚本主要分为2个
1.主要是绘制辅助点,以及拖动辅助点、绘制点之间的线段
2.绘制贝塞尔的主要逻辑,及绘制贝塞尔线
主要逻辑
public List<Transform> AnchorPoint; //添加锚点
public List<Vector3> AllPoints = new List<Vector3>(); //所有点
public List<float> segmentDistance = new List<float>(); //计算每段的长度
public float precision = 15f; //精度
//variable
private float dis; //计算距离的辅助变量
private Vector3 currentpos; //当前的坐标
private Vector3[] Pos;
public int anchorPointNum;
public int CurrentanchorPointNum;
//Help
public bool Limit = false;
public int numSegments
{
get
{
return (AllPoints.Count - 4) / 3 + 1;
}
}
public int numAllPoints
{
get {
return AllPoints.Count;
}
}
/// <summary>
/// 创建最初的路径
/// </summary>
public void CreatePath()
{
if (AllPoints.Count < 1)
{
InitPathPoint(AnchorPoint[0].position);
InitComputeDistance();
anchorPointNum = AnchorPoint.Count;
CurrentanchorPointNum = AnchorPoint.Count;
if (AnchorPoint.Count > 2)
{
for (int i = 2; i < AnchorPoint.Count; i++)
{
AddPointsToList(AnchorPoint[i].position);
ComputeDistance(i);
}
}
}
}
/// <summary>
/// 删除点
/// </summary>
public void DelectPoint()
{
if (AnchorPoint.Count>1&&CurrentanchorPointNum - AnchorPoint.Count > 0)
{
CurrentanchorPointNum = AnchorPoint.Count;
Limit = true;
segmentDistance.RemoveAt(segmentDistance.Count - 1);
for (int i = 0; i < 3; i++)
{
AllPoints.RemoveAt(AllPoints.Count - 1);
anchorPointNum = AnchorPoint.Count;
}
Limit = false;
}
}
/// <summary>
/// 移动点
/// </summary>
/// pos 是新的坐标
public void MovePoint(int index, Vector3 Pos)
{
Vector3 deltaMove = Pos - AllPoints[index]; //新坐标与旧坐标的插值
AllPoints[index] = Pos; //把旧坐标覆盖掉
if (index % 3 == 0) //判断是否是中心点
{
if (index + 1 < AllPoints.Count) //中心点的两坐标的辅助点
{
AllPoints[index + 1] += deltaMove;
}
if (index - 1 > 0)
{
AllPoints[index - 1] += deltaMove;
}
//Debug.Log("中心点");
}
else
{
bool nextPointIsAnchor = (index + 1) % 3 == 0; //区分其中的一个点
int correspondingControlIndex = (nextPointIsAnchor) ? index + 2 : index - 2; //对应点的index
int anchorIndex = (nextPointIsAnchor) ? index + 1 : index - 1; //对应的中心点
if (correspondingControlIndex > 0 && correspondingControlIndex < AllPoints.Count)
{
float dst = (AllPoints[anchorIndex] - AllPoints[correspondingControlIndex]).magnitude; //计算点与中心点的距离
Vector3 dir = (AllPoints[anchorIndex] - Pos).normalized; //计算向量的方向
AllPoints[correspondingControlIndex] = AllPoints[anchorIndex] + dir * dst; //增加的新坐标
}
//Debug.Log("辅助点");
}
UpdateDistance(index);
}
/// <summary>
/// 获取在路径上的坐标点
/// </summary>
public Vector3[] GetPointInPath(int index)
{
Vector3[] A = { AllPoints[index * 3], AllPoints[index * 3 + 1], AllPoints[index * 3 + 2], AllPoints[index * 3 + 3] };
return A;
}
/// <summary>
/// 添加新点
/// </summary>
public void AddPathPoint()
{
if (AnchorPoint.Count > 2
&& anchorPointNum - 1 - numSegments > 0
&& AnchorPoint[AnchorPoint.Count - 1].position != AnchorPoint[AnchorPoint.Count - 2].position
&& !Limit)
{
AddPointsToList(AnchorPoint[AnchorPoint.Count - 1].position);
ComputeDistance(AnchorPoint.Count - 1);
CurrentanchorPointNum = AnchorPoint.Count;
}
else
{
anchorPointNum = AnchorPoint.Count;
}
}
/// <summary>
/// 添加点到List中
/// </summary>
public void AddPointsToList(Vector3 anchorPos)
{
AllPoints.Add(AllPoints[AllPoints.Count - 1] * 2 - AllPoints[AllPoints.Count - 2]);
AllPoints.Add((AllPoints[AllPoints.Count - 1] + anchorPos) * .5f);
AllPoints.Add(anchorPos);
}
/// <summary>
///更新距离
/// </summary>
public void UpdateDistance(int index)
{
//判断是第几段的
int j = 0;
if (index < 4)
{
j = 0;
}
else
{
j = Mathf.CeilToInt((index - 4) / 3f);
}
dis = 0;
for (int i = 0; i < 4; i++)
{
dis += Vector3.Distance(AllPoints[index], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
}
segmentDistance[j]=dis;
}
/// <summary>
///计算距离
/// </summary>
public void ComputeDistance(int index)
{
dis = 0;
for (int i = 0; i < 4; i++)
{
dis += Vector3.Distance(AllPoints[index], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
}
segmentDistance.Add(dis);
}
// 初始化Path
/// <summary>
/// 计算初始点的距离3
/// </summary>
public void InitComputeDistance()
{
for (int i = 0; i < AllPoints.Count; i++)
{
dis += Vector3.Distance(AllPoints[0], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
}
segmentDistance.Add(dis);
}
/// <summary>
/// 初始化路劲点1
/// </summary>
public void InitPathPoint(Vector3 center)
{
AllPoints = new List<Vector3>
{
AnchorPoint[0].position,
AnchorPoint[0].position + (Vector3.right + Vector3.down) * precision,
AnchorPoint[1].position +(Vector3.left + Vector3.left)* precision,
AnchorPoint[1].position
};
}
/// <summary>
/// 贝塞尔方程
/// </summary>
public Vector3 EvaluateCurve(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t)
{
t = Mathf.Clamp01(t);
return (1 - t) * (1 - t) * (1 - t) * a1 + 3 * (1 - t) * (1 - t) * t * c1 + 3 * (1 - t) * t * t * c2 + t * t * t * a2;
}
/// <summary>
/// 绘制线段
/// </summary>
private void OnDrawGizmos()
{
Gizmos.color = Color.green;
if (AllPoints.Count > 0 && !Limit)
{
currentpos = AllPoints[0];
for (int j = 0; j < numSegments; j++)
{
for (int i = 0; i < segmentDistance[j]; i++)
{
float t = i / (float)segmentDistance[j];
Vector3 NextPoint = EvaluateCurve(AllPoints[j * 3], AllPoints[j * 3 + 1], AllPoints[j * 3 + 2], AllPoints[j * 3 + 3], t);
Gizmos.DrawLine(currentpos, NextPoint);
currentpos = NextPoint;
}
}
}
}
此脚本需要继承Editor,并关联上面的脚本
bool Limit = false;
Bezier bezier;
private void OnEnable()
{
bezier = (Bezier)target;
bezier.CreatePath();
}
private void OnSceneGUI()
{
Handles.color = Color.red; //设置Handles的颜色
if (bezier.AllPoints.Count > 0) //如果辅助点
{
bezier.AddPathPoint();
bezier.DelectPoint();
for (int i = 0; i < bezier.numAllPoints; i++) //绘制所有点
{
Vector3 newPos = Handles.FreeMoveHandle(bezier.AllPoints[i], Quaternion.identity, 1, Vector3.zero, Handles.SphereHandleCap);
if (bezier.AllPoints[i] != newPos)
{
bezier.MovePoint(i, newPos);
}
}
Handles.color = Color.black;
if (bezier.numSegments > 0)
{
for (int i = 0; i < bezier.numSegments; i++)
{
Vector3[] points = bezier.GetPointInPath(i);
Handles.DrawLine(points[0], points[1]);
Handles.DrawLine(points[2], points[3]);
}
}
}
}
网友评论