又一个控制角色移动 NavMeshPlayerControl
在这个的基础上:阴影实现 - 准备工作:场景中行走的角色 - yanghui01 - 博客园 (cnblogs.com),将CharacterController换成了NavMeshAgent,
不过目前功能不完善,仅作为一个参考吧,NavMeshAgent上的跳暂时还不知道怎么实现。
效果
using UnityEngine; using UnityEngine.AI; public class NavMeshPlayerControl : MonoBehaviour { private const float Dir_Speed = 10; private const float Gravity = 10; [SerializeField] private NavMeshAgent m_NavMeshAgent; [SerializeField] private Animator m_Animator; [SerializeField] private float m_MoveSpeed = 2; [SerializeField] private float m_JumpSpeed = 4; [SerializeField] private bool m_CtrlEnable = true; [SerializeField] private float m_CheckOnGroundBias = 0.1f; private bool m_WasGrounded = true; //上一帧在地面上 private bool m_IsGrounded = true; //当前在地面上 private Vector3 m_CurMoveDir = Vector3.zero; private float m_JumpTimeStamp = 0; private float m_MinJumpInterval = 0.25f; //控制跳跃间隔 private bool m_JumpInput = false; //控制一帧触发1次 private Vector3 m_VerticalSpeed = Vector3.zero; private float m_GroundY = 0; private bool m_GroundYDirty = true; void Awake() { if (!m_NavMeshAgent) m_NavMeshAgent = GetComponent<NavMeshAgent>(); if (!m_Animator) m_Animator = GetComponent<Animator>(); } void Start() { m_NavMeshAgent.updateRotation = false; //自带的太慢了, 角速度设置的很大也很慢 m_Animator.SetBool("Grounded", true); } private void Update() { if (m_CtrlEnable && !m_JumpInput && Input.GetKeyDown(KeyCode.Space)) { m_JumpInput = true; } } private void FixedUpdate() { m_Animator.SetBool("Grounded", m_IsGrounded); DirectUpdate(); m_WasGrounded = m_IsGrounded; m_JumpInput = false; } private void DirectUpdate() { float v = 0; float h = 0; if (m_CtrlEnable) { v = Input.GetAxis("Vertical"); h = Input.GetAxis("Horizontal"); } //Debug.Log($"v:{v}, h:{h}"); Transform camera = Camera.main.transform; Vector3 inputDir = camera.forward * v + camera.right * h; //摇杆上下为相机forward方向, 左右为相机right方向 float inputForce = inputDir.magnitude; //区分力度的情况 inputDir.y = 0; if (inputDir != Vector3.zero) { m_CurMoveDir = Vector3.Slerp(m_CurMoveDir, inputDir, Time.deltaTime * Dir_Speed); transform.rotation = Quaternion.LookRotation(m_CurMoveDir); m_NavMeshAgent.Move(m_CurMoveDir * m_MoveSpeed * Time.deltaTime); m_Animator.SetFloat("MoveSpeed", inputForce); //力度 } else { m_Animator.SetFloat("MoveSpeed", 0); } JumpingAndLanding(); } private void JumpingAndLanding() { bool jumpCooldownOver = (Time.time - m_JumpTimeStamp) >= m_MinJumpInterval; if (jumpCooldownOver && m_IsGrounded && m_JumpInput) //地面上按下跳, 多次跳要有一定间隔 { m_JumpTimeStamp = Time.time; m_IsGrounded = false; m_GroundYDirty = true; m_VerticalSpeed.y = m_JumpSpeed; Debug.Log($"{GetType().Name}: jump"); } if (!m_IsGrounded) //不在地面上时, 要不断下落 { m_NavMeshAgent.Move(m_VerticalSpeed * Time.deltaTime); m_IsGrounded = CheckOnGround(); if (m_IsGrounded) { m_VerticalSpeed.y = 0; } else { m_VerticalSpeed.y -= Gravity * Time.deltaTime; } } else //在地面上时, 也要不断检测是否需要下落(比如: 从一个高的平台走到低的平台) { if (!CheckOnGround()) { m_IsGrounded = false; m_GroundYDirty = true; m_VerticalSpeed.y = 0; } } if (!m_WasGrounded && m_IsGrounded) //之前不在地面上, 现在在地面上了 { m_Animator.SetTrigger("Land"); } if (!m_IsGrounded && m_WasGrounded) //之前在地面上的, 现在不在地面上了 { m_Animator.SetTrigger("Jump"); } } private bool CheckOnGround() { var pos = this.transform.position; pos.y += m_NavMeshAgent.radius; pos.y -= m_CheckOnGroundBias; var isHit = Physics.CheckSphere(pos, m_NavMeshAgent.radius, 1 << LayerMask.NameToLayer("Ground")); Debug.DrawLine(pos, pos + Vector3.down * m_NavMeshAgent.radius, Color.red, 0.05f); return isHit; } public float GetGroundY() { var playerPos = this.transform.position; if (!m_IsGrounded) { if (m_GroundYDirty) { bool isHit = Physics.Raycast(playerPos, Vector3.down, out var hitInfo, 5); if (isHit) m_GroundY = hitInfo.point.y; m_GroundYDirty = false; } return m_GroundY; } return playerPos.y; } }