又一个控制角色移动 NavMeshPlayerControl

发布时间 2023-10-30 23:02:58作者: yanghui01

在这个的基础上:阴影实现 - 准备工作:场景中行走的角色 - 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;
    }

}