使用NavMesh进行移动

发布时间 2023-10-28 00:53:46作者: yanghui01

效果

 

using UnityEngine;
using UnityEngine.AI;

public class NavMeshMoveControl : MonoBehaviour
{
    private NavMeshAgent m_NavMeshAgent;
    private Animator m_Animator;

    public float m_StopDist = 0f;

    private Vector3 m_TargetPos = Vector3.zero;
    private Vector3 m_TargetForward = Vector3.zero;
    private bool m_CheckArriveTarget = false;

    void Awake()
    {
        m_NavMeshAgent = GetComponent<NavMeshAgent>();
        m_Animator = GetComponent<Animator>();
    }

    void Start()
    {
        m_NavMeshAgent.updateRotation = false; //自带的太慢了, 角速度设置的很大也很慢
        m_Animator.SetBool("Grounded", true);
    }

    void OnEnable()
    {
        if ("running" == GetNavStatus())
        {
            m_NavMeshAgent.Warp(m_TargetPos); //瞬移到目标位置
        }
    }

    void OnDisable()
    {
        if (m_Animator != null)
        {
            m_Animator.logWarnings = false;
        }
    }

    void Update()
    {
        if (m_CheckArriveTarget && !m_NavMeshAgent.pathPending)
        {
            var tTransform = this.transform;
            tTransform.forward = Vector3.Slerp(tTransform.forward, m_TargetForward, Time.deltaTime * 10);

            if (m_NavMeshAgent.remainingDistance <= m_StopDist)
            {
                m_CheckArriveTarget = false;
                OnPlayStandAnim();
                //m_NavMeshAgent.enabled = false;
            }
        }
    }

    public void NavToPosition(Vector3 toPos)
    {
        var myPos = transform.position;
        toPos.y = myPos.y; //y方向坐标不变
        var distVec = toPos - myPos;
        if (distVec.sqrMagnitude <= 0)
        {
            Debug.LogWarning("pos not change");
            return;
        }

        if (m_NavMeshAgent.enabled)
        {
            m_NavMeshAgent.enabled = false; //寻路过程中, 切换成新的寻路点, 如果不调用enable=false, 有惯性, 看着很奇怪
        }
        m_NavMeshAgent.enabled = true;
        if (m_NavMeshAgent.isOnNavMesh)
        {
            m_TargetForward = distVec.normalized;
            m_TargetPos = toPos;
            m_CheckArriveTarget = true;

            m_NavMeshAgent.SetDestination(toPos); //在enable后调用
            m_NavMeshAgent.isStopped = false;
            OnPlayMoveAnim();
        }
        else
        {
            Debug.LogWarning($"{this.name} isOnNavMesh=false, can't SetDestination");
        }
    }

    //当前寻路状态

    public string GetNavStatus()
    {
        if (m_NavMeshAgent.isOnNavMesh && m_NavMeshAgent.remainingDistance > 0)
        {
            return m_NavMeshAgent.isStopped ? "paused" : "running";
        }
        return "stopped";
    }

    //暂停寻路
    public void PauseNav()
    {
        if ("paused" == GetNavStatus()) return;

        if (m_NavMeshAgent.isOnNavMesh)
        {
            m_NavMeshAgent.isStopped = true; //暂停寻路
        }
        OnPlayStandAnim();
    }

    //强制停止(会清除寻路数据)
    public void StopNav()
    {
        if ("stopped" == GetNavStatus()) return;

        if (m_NavMeshAgent.hasPath)
        {
            m_NavMeshAgent.ResetPath(); //在disable前调用
            //m_NavMeshAgent.enabled = false;
        }
        OnPlayStandAnim();
    }

    public void PrintInfo()
    {
        Debug.Log($"onNavMesh:{m_NavMeshAgent.isOnNavMesh}, hasPath:{m_NavMeshAgent.hasPath}, pending:{m_NavMeshAgent.pathPending}, status:{m_NavMeshAgent.pathStatus}");
        if (m_NavMeshAgent.isOnNavMesh)
        {
            Debug.Log($"remain:{m_NavMeshAgent.remainingDistance}, stopped:{m_NavMeshAgent.isStopped}");
        }
    }

    protected virtual void OnPlayStandAnim()
    {
        m_Animator.SetFloat("MoveSpeed", 0);
    }

    protected virtual void OnPlayMoveAnim()
    {
        m_Animator.SetFloat("MoveSpeed", 1);
    }

}

移动到点击位置代码

using UnityEngine;

public class MouseClickMove : MonoBehaviour
{

    void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hit;
            var ray = Camera.main.ScreenPointToRay(Input.mousePosition); //摄像机方向发射1条射线
            Debug.DrawRay(ray.origin, ray.direction * 20, Color.yellow); //画出这条射线

            var maxDistance = 50;
            if (Physics.Raycast(ray, out hit, maxDistance)) //检测射线是否碰到地面
            {
                Debug.Log($"hit pos: {hit.point}");
                GetComponent<NavMeshMoveControl>().NavToPosition(hit.point);

                var clickPosGo = GameObject.Find("ClickPos");
                if (clickPosGo)
                    clickPosGo.transform.position = hit.point;
            }
        }
    }

}

相机控制,相机跟随相关的参考这边

阴影实现 - 准备工作:场景中行走的角色 - yanghui01 - 博客园 (cnblogs.com)