前言
之前有个需求,要求一个或多个物体按照规定的路径移动。我根据灵魂重新大神所写的脚本做了一点点修改,最终完成了这个需求。
原理
本文内的简单移动不涉及曲线等复杂的东西,所以可以用一个数组保存移动的坐标点,而后通过计算位移坐标的方式依次移动。
实现
1. PathDefine.cs
PathDefine脚本用来存储一条移动路径,并将路径点在Editor窗口中通过DrawGizmos绘制出来。它的代码非常简单,核心代码只有一行。
public class PathDefine : MonoBehaviour {
public Transform[] pathPoints;
# if UNITY_EDITOR
GameObject go;
private void OnDrawGizmos()
{
if(pathPoints == null || pathPoints.Length < 2)
{
return;
}
var pt = pathPoints.Where(t => t != null).ToList();
if(pt.Count < 2)
{
return;
}
for (int i = 1; i < pt.Count; i++)
{
Gizmos.DrawLine(pt[i - 1].position, pt[i].position);
Vector3 v1 = Vector3.zero, v2 = Vector3.zero;
var temp = pt[i].position - pt[i - 1].position;
Vector3 pos = (pt[i].position + pt[i - 1].position) / 2;
if(go == null)
{
go = GameObject.Find("PathHelpPoint");
if (go == null)
{
go = new GameObject();
go.name = "PathHelpPoint";
go.transform.parent = this.transform;
}
}
go.transform.position = pos;
go.transform.LookAt(pt[i]);
//获取箭头方向向量
v1 = Quaternion.AngleAxis(135,go.transform.up) * temp;
v2 = Quaternion.AngleAxis(225, go.transform.up) * temp;
//画箭头
Gizmos.DrawLine(pos, Vector3.Normalize(v1) * 0.2f + pos);
Gizmos.DrawLine(pos, Vector3.Normalize(v2) * 0.2f + pos);
}
}
#endif
}
OnDrawGizmos 使得所有路径点均以线段连接,并有箭头指示连接方向。实际效果如下图所示:
2.FollowPath.cs
FollowPath脚本是移动物体要挂载的脚本,它提供了几个Public字段用来控制移动的参数。在完成移动时,它提供了一个Action回调事件。
public class FollowPath : MonoBehaviour {
public enum RepeatType
{
Single = 0,
PingPong = 1,
SimpleRepeat = 2
}
public enum MoveType {
MoveTowards = 0,
Lerp = 1
}
public PathDefine path;
public RepeatType repeatType = RepeatType.Single;
public MoveType moveType = MoveType.Lerp;
public bool isMove = true;
public float moveSpeed = 0.1f;
public float delayTime = 0f;
public float JudgeDistance = 0.005f;
public Action<GameObject> OnMoveEnd;
private int index = -1;
private Transform nextPoint;
private float waitTime = 0f;
private float nowDistance = 0f;
private int addNum = 1;
private Transform m_Transform;
// Use this for initialization
void Start () {
index = -1;
waitTime = delayTime;
addNum = 1;
m_Transform = this.transform;
if (path)
{
index = 0;
nextPoint = path.pathPoints[index];
m_Transform.position = nextPoint.position;
}
}
// Update is called once per frame
void Update () {
if (waitTime >= 0)
{
waitTime -= Time.deltaTime;
return;
}
if(path && isMove)
{
if (nextPoint)
{
if(moveType == MoveType.Lerp)
m_Transform.position = Vector3.Lerp(m_Transform.position, nextPoint.position, Time.deltaTime * moveSpeed);
else if(moveType == MoveType.MoveTowards)
m_Transform.position = Vector3.MoveTowards(m_Transform.position, nextPoint.position, Time.deltaTime * moveSpeed);
nowDistance = Vector3.Distance(m_Transform.position, nextPoint.position);
if(nowDistance <= JudgeDistance)
{
index+= addNum;
if(index >= path.pathPoints.Length)
{
Debug.Log("完了");
switch (repeatType)
{
case RepeatType.Single:
addNum = 1;
index = 0;
isMove = false;
break;
case RepeatType.PingPong:
addNum = -1;
index -= 1;
break;
case RepeatType.SimpleRepeat:
addNum = 1;
index = 0;
m_Transform.position = this.path.pathPoints[0].position;
break;
default:
isMove = false;
break;
}
if (OnMoveEnd != null)
{
OnMoveEnd(this.gameObject);
}
}else if(index <= 0)
{
addNum = 1;
index = 0;
}
nextPoint = this.path.pathPoints[index];
}
}
}
}
public void ReStart()
{
index = -1;
waitTime = delayTime;
addNum = 1;
m_Transform = this.transform;
if (path)
{
index = 0;
nextPoint = path.pathPoints[index];
m_Transform.position = nextPoint.position;
}
isMove = true;
}
}
引用资料
- [头图]【Unity】Unity-Japan UnityChanSD角色
- [脚本]【博客园】灵魂重新 Follow Path-》Unity3d通用脚本