玩家状态脚本:

using UnityEditor.UI;

using UnityEngine;

// 基类,各个状态都继承这个类
public abstract class PlayerState
{
    protected PlayerController controller;

    public PlayerState(PlayerController controller)
    {
        this.controller = controller;
    }

    // 在进入状态时触发
    public virtual void Enter() { }

    // 在每个_frame更新时触发,状态可能会切换
    public virtual void Update() { }

    // 每个fixedupdate
    public virtual void FixedUpdate() { }

    // 离开状态时触发
    public virtual void Exit() { }
}

// 反转运动状态
protected void ChangeToIdleCondition()
{
    // 如果没有移动输入,切换到 idle状态
    if (controller.GetMoveInput().magnitude < 0.1)
    {
        controller.ChangeState(new IdleState(controller));
    }
}

// 反转运动状态
protected void ChangeToMoveCondition()
{
    // 如果有移动输入且有地面
    if (controller.GetMoveInput().magnitude > 0f && controller.IsGrounded())
    {
        controller.ChangeState(new MoveState(controller));
    }
}

// 反转跳跃状态
protected void ChangeToJumpCondition()
{
    // 如果有跳跃输入且有地面
    if (controller.GetJumpInput() && controller.IsGrounded())
    {
        controller.ChangeState(new JumpState(controller));
    }
}

// 执行运动动作
protected void MovePlayer()
{
    // 当前速度
    float currentSpeed = controller.GetSprintInput() ? controller.GetPlayerSprintSpeed() : controller.GetPlayerSpeed();

    // 得到摄像机的变换
    Transform cameraTransform = controller.GetCameraTransform();
    // 得到前方和右方方向
    Vector3 cameraForward = cameraTransform.forward;
    Vector3 cameraRight = cameraTransform.right;

    // 将y设置为0
    cameraForward.y = 0f;
    cameraRight.y = 0f;

    // 正规化
    cameraForward.Normalize();
    cameraRight.Normalize();

    // 移动输入
    Vector2 moveInput = controller.GetMoveInput();

    // 计算移动方向
    Vector3 moveDirection = (cameraForward * moveInput.y + cameraRight * moveInput.x).normalized;

    // 移动物体
    controller.GetRigidbody().linearVelocity = new Vector3(moveDirection.x * currentSpeed, controller.GetRigidbody().linearVelocity.y, moveDirection.z * currentSpeed);
}

protected void ApplyDownwardsForce()
{
    // 如果没有地面
    if (!controller.IsGrounded())
    {
        // 向下施加力
        controller.GetRigidbody().AddForce(Vector3.down * controller.GetPlayerDownwardsForce());
    }
}
}

// 空闲状态
public class IdleState : PlayerState
{
    public IdleState(PlayerController controller) : base(controller) { }

    // 进入时触发
    public override void Enter()
    {
        // 停止运动
        controller.GetRigidbody().linearVelocity = [Vector3.zero];
    }

    // 每个_frame更新时触发
    public override void Update()
    {
        // 反转运动状态和跳跃状态
        ChangeToMoveCondition();
        ChangeToJumpCondition();
    }

    // 每个fixedupdate
    public override void FixedUpdate()
    {
        // 向下施加力
        ApplyDownwardsForce();
    }
}

// 移动状态
public class MoveState : PlayerState
{
    public MoveState(PlayerController controller) : base(controller) { }

    // 每个_frame更新时触发
    public override void Update()
    {
        // 反转运动状态和跳跃状态
        ChangeToIdleCondition();
        ChangeToJumpCondition();
    }

    // 每个fixedupdate
    public override void FixedUpdate()
    {
        // 执行运动动作
        MovePlayer();
        // 向下施加力
        ApplyDownwardsForce();
    }
}

// 跳跃状态
public class JumpState : PlayerState
{
    public JumpState(PlayerController controller) : base(controller) { }

    // 进入时触发
    public override void Enter()
    {
        // 应用跳跃力
        controller.GetRigidbody().linearVelocity = new Vector3(controller.GetRigidbody().linearVelocity.x, controller.GetPlayerJumpHeight(), controller.GetRigidbody().linearVelocity.z);
    }

    // 每个_frame更新时触发
    public override void Update()
    {
        // 如果有地面
        if (controller.IsGrounded())
        {
            // 反转运动状态和跳跃状态
            ChangeToIdleCondition();
            ChangeToMoveCondition();
        }
    }

    // 每个fixedupdate
    public override void FixedUpdate()
    {
        // 执行运动动作
        MovePlayer();
    }
}

玩家控制器脚本:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    // movement
    [Header("Movement Settings")]
    public float playerSpeed = 5f;
    public float playerSprintSpeed = 10f;
    private float playerDownwardsForce = 50f;
    private float playerJumpHeight = 10f;

    // ground check
    [Header("Ground Check Settings")]
    public Transform groundCheck;
    public LayerMask groundLayer;
    [SerializeField] private float groundCheckRadius = 0.2f;

    // camera
    [Header("Camera")]
    public Transform orientation;

    // input
    PlayerInput playerInput;
    InputAction moveAction;
    Rigidbody rb;
    bool isGrounded;
    Vector2 moveInput;

    // state machine
    private PlayerState currentState;

    void Start()
    {
        // 初始化变量...
        rb = GetComponent<Rigidbody>();
        playerInput = GetComponent<PlayerInput>();
        moveAction = playerInput.actions.FindAction("Movement");

        // 初始化为Idle状态
        ChangeState(new IdleState(this));
    }

    void Update()
    {
        // 读取输入
        if (moveAction != null)
        {
            moveInput = moveAction.ReadValue<Vector2>();
        }

        // 地面检测
        isGrounded = Physics.Raycast(groundCheck.position, Vector3.down, groundCheckRadius);

        // 呼叫当前状态的Update
        currentState.Update();
    }

    private void FixedUpdate()
    {
        // 呼叫当前状态的FixedUpdate
        currentState.FixedUpdate();
    }

    // state machine core
    public void ChangeState(PlayerState newState)
    {
        // 离开当前状态
        currentState.Exit();

        // 更新当前状态
        currentState = newState;
        currentState.Enter();
    }

    // public getters
    public Vector2 GetMoveInput() => moveInput;
    public bool IsGrounded() => isGrounded;
    public Rigidbody GetRigidbody() => rb;
    public Transform GetCameraTransform() => orientation;
    public Transform GetPlayerTransform() => transform;

    public float GetPlayerSpeed() => playerSpeed;
    public float GetPlayerSprintSpeed() => playerSprintSpeed;
    public float GetPlayerDownwardsForce() => playerDownwardsForce;
    public float GetPlayerJumpHeight() => playerJumpHeight;

    public bool GetSprintInput() => playerInput.actions.FindAction("Sprinting").ReadValue<float>() == 1f;
    public bool GetJumpInput() => playerInput.actions.FindAction("Jumping").ReadValue<float>() == 1f;
}