NavMeshAgent 寻路导航组件

NavMesh地面的烘焙方法


1.选中要地面 或者 地图上的静态障碍物:树\房子\石头等

2.在U3D右边Inspector面板右上角Static旁边的倒三角 ,选中 Navigation static 勾,  表示地面导航层 ,把当前选中物体设置为导航层

3.菜单栏Window ->Navigation ,Inspector面板 右下角 Bake烘焙,生成导航路径数据

4.然后要移动的角色身上添加 NavMeshAgent组件,就可以导航移动角色

 

NavMeshAgent 组件面板属性

 

    agent.updateRotation = false;   //不允许NavMesh来旋转角色  
    agent.updatePosition = true;  //允许NavMesh来移动角色  

   agent.velocity.magnitude 这个也是速度, GetComponent<Animator>().SetFloat("Speed", agent.velocity.magnitude);

    speed                        移动速度

    Angular Speed             转角速度 ,转身速度    角速度: 最高转速(度/秒)。

    Acceleration                 加速度,启动时的 最大加速度。

    Stopping Distance         停止距离 ,,制动距离:制动距离。到目的地的距离小于这个值,代理减速。

    Auto Traverse OffMesh Link 自动遍历OffMesh链接:自动移动并关闭OffMeshLinks

    Auto Repath                 自动重新寻路:如果现有的部分已失效,获得新的路径。

    Height                         高度:代理的高度(用于调试图形)。

    Base offset                   基本偏移:碰撞几何体相对于实际几何体垂直的偏移。

    Obstacle Avoidance Type 障碍躲避类型 :躲避的质量水平。

    NavMesh Walkable          导航网格行走:指定代理可以遍历的导航网格层类型。这个参数很有用,在接下来的实例中可以用到。


 

注意事项

       1.启用组件,即使角色在站立状态,也会轻微为角色添加移动,非常小的,但是会影响刚体的速度的属性.最好开始移动时 启用NavMeshAgent,移动结束关闭NavMeshAgent.

       2. 使用了NavMeshAgent就不能用 transform.position = newPos;因为上面说了NavMeshAgent不停的为角色坐标赋值,用 WarpPoint(newPos)代替或者移动赋值前先NavMeshAgent.enabled = false。

     3.在使用Rigidbody.AddForce施加外力时,首先关闭NavMeshAgent,重置刚体速度Rigidbody.velocity = Vector3.zero,然后在下一帧内对刚体加力,不能在同一帧进行!!!

 

常用函数和变量

    remainingDistance     //寻路剩余距离,角色和目标点的距离,(有时候为0,需要与Vector3.Distance()效验)

    stoppingDistance     //停止距离

    SetDestination(Point);//设置要移动的目标点

    destination          //设置要移动的目标点

    Stop();           //停止移动,寻路停止

    Resume          //Stop之后用Resume来恢复

    path.corners;//储存路径,路径坐标点数组

    ResetPath();  //重置路径

    pathStatus   //路径状况 ,当前路况的状态(完整,局部或者无效)。

        PathComplete    在目的地的路径终止。The path terminates at the destination.

        PathPartial        路径中不能到达目的地。The path cannot reach the destination.

        PathInvalid        路径无效。The path is invalid.

    velocity.magnitude //移动速度

 

 

//移动到B角色面前,用角色和B角色的包围盒的最近点,这样就不用修改 stoppingDistance停止距离

    agent.stoppingDistance = 敌人包围盒半径的长度;

    agent.destination = target.collider.ClosestPointOnBounds(transform.position);

 

 

 

判断是否正在移动中

    /// <summary>
    /// 是否在移动中
    /// </summary>
    /// <returns></returns>
    public bool IsMoving
    {
        get
        {
            if (agent != null && agent.enabled)
            {
                remainingDistance = Vector3.Distance(Transform.position, agent.destination);
                return  agent.pathPending || remainingDistance > agent.stoppingDistance || agent.velocity != Vector3.zero;
            }

             return  false;
        }
    }


    /// 是否移动结束
    bool EventMoveEnd() {
        return state == "MOVING" && !IsMoving();

  }

 

绘制移动路线

/// <summary>
/// 绘制移动路线
/// </summary>
void OnDrawGizmos() 
{
    var agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
    var path = agent.path;
    Color c = Color.white;
    switch (path.status)
    {
        case UnityEngine.AI.NavMeshPathStatus.PathComplete: c = Color.white; break;
        case UnityEngine.AI.NavMeshPathStatus.PathInvalid: c = Color.red; break;
        case UnityEngine.AI.NavMeshPathStatus.PathPartial: c = Color.yellow; break;
    }
    for (int i = 1; i < path.corners.Length; ++i)
        Debug.DrawLine(path.corners[i-1], path.corners[i], c);
}

   

状态切换

            //取消移动
            agent.ResetPath();
            return "IDLE";

           //角色死亡
            agent.ResetPath(); 
        
            //移动到点
            agent.stoppingDistance = 0.1f;
            agent.destination = pos;
            return "MOVING";

              // 移动到敌人包围盒前
              agent.stoppingDistance = target.agent.radius;
              agent.destination = target.collider.ClosestPointOnBounds(transform.position);
              return "MOVING";  

using System;
using UnityEngine;
using UnityEngine.AI;
using Debug = UnityEngine.Debug;

public class MoveNavMeshAgent : MonoBehaviour
{
    public NavMeshAgent agent;
    [SerializeField]
    bool _isMoveing = false;
    [SerializeField]
    float remainingDistance;
    [SerializeField]
    Vector3 curr_destination;
    [SerializeField]
    Vector3 last_destination;
    private Transform Transform;

    void Awake()
    {
        agent = gameObject.GetComponent<NavMeshAgent>();
        if(!agent)Debug.LogError(gameObject.name + " no NavMeshAgent compent");
        Transform = gameObject.transform;
        agent.updateRotation = true; //允许NavMesh来旋转角色
        agent.updatePosition = true; //允许NavMesh来移动角色
        agent.enabled = false;
    }


    /// <summary>
    ///使用了NavMeshAgent就不能用 transform.position = newPos;用 WarpPoint(newPos)代替或者移动赋值前先NavMeshAgent.enabled = false
    /// </summary>
    /// <param name="destination"></param>
    public void WarpPoint(Vector3 destination)
    {
        if (agent.enabled == false)
        {
            agent.enabled = true;
        }
        agent.Warp(destination);
        agent.enabled = false;
    }

    /// <summary>
    /// 是否在移动中
    /// </summary>
    /// <returns></returns>
    public bool IsMoving
    {
        get
        {
            if (agent != null && agent.enabled)
            {
                remainingDistance = Vector3.Distance(Transform.position, agent.destination);
                _isMoveing = agent.pathPending || remainingDistance > agent.stoppingDistance || agent.velocity != Vector3.zero;
            }
            else
            {
                _isMoveing = false;
            }
            return _isMoveing;
        }
    }

    /// <summary>
    /// 是否移动结束
    /// </summary>
    /// <returns></returns>
    public bool IsMoveEnd
    {
        get
        {
            return !IsMoving;
        }
    }

    //停止移动
    public void StopMove()
    {
        if (agent.enabled == true) //停止NavMeshAgent ,停止移动
        {
            agent.ResetPath();
            agent.Stop();
            agent.enabled = false;
        }
    }

    /// <summary>
    /// 移动到点
    /// </summary>
    public void MovePoint(Vector3 destination, float stoppingDistance = 0.17f)
    {
        curr_destination = destination;
        if (agent.enabled && curr_destination == last_destination && IsMoving && Math.Round(stoppingDistance, 2) == Math.Round(agent.stoppingDistance, 2))//与上一次目的地相同,就跳过
        {
            return;
        }
        if (agent.enabled == false)
        {
            agent.enabled = true;
        }
        agent.stoppingDistance = stoppingDistance;
        agent.destination = destination;
        last_destination = curr_destination;
    }

    /// <summary>
    /// 移动到距离敌人包围盒最近的点,如果是远程英雄的话 stoppingDistance  就是攻击距离,
    /// </summary>
    /// <param name="stoppingDistance"></param>
    /// <param name="enemyCollider"></param>
    public void MoveToEnemy(Collider enemyCollider, float stoppingDistance = 0.17f)
    {
        Vector3 destination = enemyCollider.ClosestPointOnBounds(Transform.position);
        MovePoint(destination, stoppingDistance);
    }

    /// <summary>
    /// 绘制移动路线
    /// </summary>
    void OnDrawGizmos()
    {
        if (agent != null && agent.enabled == true)
        {
            var path = agent.path;
            // color depends on status
            Color c = Color.white;
            switch (path.status)
            {
                case UnityEngine.AI.NavMeshPathStatus.PathComplete:
                    c = Color.white;
                    break;

                case UnityEngine.AI.NavMeshPathStatus.PathInvalid:
                    c = Color.red;
                    break;

                case UnityEngine.AI.NavMeshPathStatus.PathPartial:
                    c = Color.yellow;
                    break;
            }
            // draw the path
            for (int i = 1; i < path.corners.Length; ++i)
                Debug.DrawLine(path.corners[i - 1], path.corners[i], c);
        }

    }

    public void FixedUpdate()
    {
#if UNITY_EDITOR
        var ShowState_Debug = IsMoving;
#endif
    }
}

 

  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值