前言

之前有个需求,要求一个或多个物体按照规定的路径移动。我根据灵魂重新大神所写的脚本做了一点点修改,最终完成了这个需求。

原理

本文内的简单移动不涉及曲线等复杂的东西,所以可以用一个数组保存移动的坐标点,而后通过计算位移坐标的方式依次移动。

实现

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;
    }
}

路径图

引用资料

  1. [头图]【Unity】Unity-Japan UnityChanSD角色
  2. [脚本]【博客园】灵魂重新 Follow Path-》Unity3d通用脚本

我来吐槽

*

*