效果
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)