路点寻路(unity3D)

路点寻路

第一步:现在场景上确认几个定点,并用Gizmos画出路线

首先在场景中确定几个空节点,放在一个父节点下面

将脚本绑在该父节点上

路点寻路(unity3D)_第1张图片

画出来的效果:

路点寻路(unity3D)_第2张图片

public class WayPoint : MonoBehaviour
{
    //用一个数组对点进行存储
    Transform[] points;

    public Transform[] Points
    {
        get
        {
            if(points == null)
            {
                points = new Transform[transform.childCount];
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = transform.GetChild(i);
                }
            }
            return points;
        }
    }
    
    // Start is called before the first frame update
    void Start()
    {
     
    }

    // Update is called once per frame
    void Update()
    {
        
    }


    //和update类似,也是每帧调用一次
    //OnDrawGizmos是在代码编译完成时开始执行
    private void OnDrawGizmos()
    {
        //Gizmos.DrawCube(Vector3.zero, Vector3.one);
        //划线的颜色
        Gizmos.color = Color.red;
        
        for (int i = 0; i < Points.Length - 1; i++)
        {
            Gizmos.DrawLine(Points[i].position, Points[i + 1].position);
        }
    }
}

思考:

  1. 用数组对行动的点进行存储
  2. OnDrawGizmos() 在代码编译的时候执行,然后调用属性函数,获取到用于画线的点
  3. 每帧执行,每帧都在画线

第二步:物体的移动

主要用的api:

  1. transform.LookAt() 物体的朝向
  2. Vector3.MoveTowards(起点坐标,终点坐标,最大步长),当距离终点较远时,每步走最大的步长的长度,返回要移动的位置
  3. Vector3.Distance() 计算两个向量的间距
  4. Vector3.LerpVector3 a, Vector3 b, float t)插值
  5. Vector3.Cross() 法线
public class Patrol: MonoBehaviour
{
    //当前怪物所在的路径,以及路径信息
    public WayPoint way;
    //要移动到的路径点的下标
    int targetIndex;
    //移动速度
    public float moveSpeed;

    //旋转的速度
    public float rotateSpeed;

    //要旋转的目标的朝向
    Vector3 targetDir;

    // Start is called before the first frame update
    void Start()
    {
        transform.position = way.Points[0].position;
        transform.LookAt(way.Points[1]);
        targetIndex = 1;

        targetDir = way.Points[targetIndex].position - transform.position;
    }

    // Update is called once per frame
    void Update()
    {

        //transform.position += transform.forward * moveSpeed * Time.deltaTime;
        //maxDistanceDelta最大的步长
        //Vector3.MoveTowards(起点坐标,终点坐标,最大步长),当距离终点较远时,每步走最大的步长的长度,返回要移动的位置
        //当靠近或抵达终点时,只会返回终点的坐标,不会越过
        transform.position = Vector3.MoveTowards(transform.position, way.Points[targetIndex].position, moveSpeed * Time.deltaTime);
        //Rotate();
        if (Vector3.Distance(transform.position, way.Points[targetIndex].position) <= 0.1f)
        {
            targetIndex++;
            if (targetIndex >= way.Points.Length)
            {
                this.enabled = false;
                return; //用return的原因是,更改enabled状态后,下一帧不会执行,这一帧会执行完
            }
            targetDir = way.Points[targetIndex].position - transform.position;
           
           // transform.LookAt(way.Points[targetIndex]);
        }

        transform.forward = Vector3.Lerp(transform.forward, targetDir, rotateSpeed * Time.deltaTime);

    }
    
    void Rotate()
    {
        Vector3 dir = way.Points[targetIndex].position - transform.position;
        float angle = Vector3.Angle(transform.forward, dir);

        angle = Mathf.Min(angle, rotateSpeed);

        transform.Rotate(Vector3.Cross(transform.forward, dir)* angle);
    }
}


思路:

主要是关于人物的向目标点的移动和移动过程中的转向问题

移动的问题:

  1. 首先要知道路线,可以通过静态的绑定或者动态的查找,主要是获取绑定上面路线脚本的节点,通过脚本组件里面的属性获取到路线点

  2. 定义一个变量存储要到达的点在数组中的的下标,定义移动的速度

  3. 在start里面,首先设置物体的位置到第一个点,物体朝向第二个点,并且将下标加一

  4. 最重要的就是移动,有两种方法:

    第一种:最简单的,因为角度确定,移动是向前的,所以每帧去加上一个向前的向量即可

    第二种:利用Vector3.MoveTowardsAPI,可以避免因为速度过快,而超出去的情况

  5. 转向的校验:通过物体与目标点的向量的间距来判断是否转向,转向将下标加一,并且还要校验下标是否越界,如果越界,就关掉脚本的活性,并且跳出语句。

转向的问题:

  1. 转向的问题也有几种解决办法

    第一种:直接通过transform.LookAt()来更改物体的朝向

    第二种:写一个更改角度的函数,首先获取物体与目标点间的向量,然后计算出物体向前的向量与获取向量的夹角,设置一个旋转角度,根据旋转角度不停的旋转方向,直到夹角为0;

    注意:①这里在旋转的时候,不能绕着y轴旋转,而是绕着法向量进行旋转,因为角度有正有负

    ​ ②可以优化的地方,在旋转之前,将两向量的夹角与设定的旋转的角度进行比较,选择较小的角进行旋转,可以避免旋转过的情况出现

    第三种:插值的方法,将forword的方向与目标点的方向,进行插值改变

你可能感兴趣的