如上图为状态机的例子
而在我们自己做的项目中,可以通过创建以下文件来实现状态机管理效果

  1. player
    row
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class Player : MonoBehaviour
    {
    [Header("Attack details")]
    public Vector2[] attackMovement;

    public bool isBusy { get; private set; }

    [Header("Move info")]
    public float moveSpeed = 12f;
    public float jumpForce;

    [Header("Dash info")]
    public float DashSpeed;
    public float DashDuration;
    public float dashDir { get; private set; }
    [SerializeField] private float dashCoolDown;
    private float dashUsageTimer;

    #region Collision
    [Header("Collision info")]
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundCheckDistance;
    [SerializeField] private Transform wallChcek;
    [SerializeField] private float wallCheckDistance;
    [SerializeField] private LayerMask whatIsGround;
    #endregion

    #region Components
    public Animator animator { get; private set; }
    #endregion

    #region States
    public PlayerStateMachine StateMachine { get; private set; }

    public PlayerIdleState StateIdle { get; private set; }

    public PlayerMoveState MoveState { get; private set; }

    public PlayerJumpState JumpState { get; private set; }

    public PlayerAirState AirState { get; private set; }

    public PlayerDashState DashState { get; private set; }

    public PlayerWallSlideState WallSlide { get; private set; }

    public PlayerWallJumpState WallJumpState { get; private set; }

    public PlayerPrimaryAttackState PrimaryAttack { get; private set; }

    #endregion

    public int facingDir { get; private set; } = 1;
    private bool facingRight = true;




    public Rigidbody2D rb { get; private set; }
    private void Awake()
    {
    StateMachine = new PlayerStateMachine();

    StateIdle = new PlayerIdleState(this,StateMachine,"Idle");
    MoveState = new PlayerMoveState(this, StateMachine, "Move");

    JumpState = new PlayerJumpState(this, StateMachine, "Jump");
    AirState = new PlayerAirState(this, StateMachine, "Jump");
    DashState = new PlayerDashState(this, StateMachine, "Dash");
    WallSlide = new PlayerWallSlideState(this, StateMachine, "WallSlide");
    WallJumpState = new PlayerWallJumpState(this, StateMachine, "Jump");
    PrimaryAttack = new PlayerPrimaryAttackState(this, StateMachine, "Attack");


    }

    public void Start()
    {
    animator = GetComponentInChildren<Animator>();

    StateMachine.Initialize(StateIdle);

    rb = GetComponent<Rigidbody2D>();

    }

    public void Update()
    {
    StateMachine.currentState.Update();
    CheckForDashInput();
    }

    public IEnumerator BusyFor(float _seconds)
    {
    isBusy = true;
    yield return new WaitForSeconds(_seconds);
    isBusy = false;
    }



    public void CheckForDashInput()
    {
    if (IsWallDetected())
    return;
    dashUsageTimer -= Time.deltaTime;
    if (Input.GetKeyDown(KeyCode.LeftShift) && dashUsageTimer < 0)
    {
    dashUsageTimer = dashCoolDown;
    dashDir = Input.GetAxisRaw("Horizontal");

    if(dashDir == 0)
    {
    dashDir = facingDir;
    }
    StateMachine.ChangeState(DashState);
    }
    }
    #region Collision
    public bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);

    public bool IsWallDetected() => Physics2D.Raycast(wallChcek.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
    #endregion
    #region Velocity
    public void SetVelocity(float _xVelocity, float _yVelocity)
    {
    rb.velocity = new Vector2(_xVelocity, _yVelocity);
    FlipController(_xVelocity);
    }
    public void ZeroVelocity() => rb.velocity = new Vector2(0, 0);
    #endregion

    #region Flip
    public void Flip()
    {
    facingDir *= -1;
    facingRight = !facingRight;
    transform.Rotate(0, 180, 0);
    }

    public void FlipController(float _x)
    {
    if (_x>0 && !facingRight)
    {
    Flip();
    }
    else if(_x<0 && facingRight)
    {
    Flip();
    }
    }
    #endregion

    #region Trigger
    public void AnimationTrigger () => StateMachine.currentState.AnimationFinishTrigger();
    #endregion
    }


    Player 为玩家类,创建各种状态实体并管理
  2. PlayerState
    row
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class PlayerState
    {
    protected PlayerStateMachine stateMachine;
    protected Player player;
    protected Rigidbody2D rb;
    public float xInput;
    public float yInput;
    private string animBoolName;
    protected float stateTimer;
    protected bool triggerCalled;
    public PlayerState(Player _player,PlayerStateMachine _stateMachine, string _animBoolName)
    {
    this.player = _player;
    this.stateMachine = _stateMachine;
    this.animBoolName = _animBoolName;
    }

    public virtual void Enter()
    {
    player.animator.SetBool(animBoolName,true);
    rb = player.rb;
    triggerCalled = false;
    }

    public virtual void Update()
    {
    stateTimer -= Time.deltaTime;
    xInput = Input.GetAxisRaw("Horizontal");
    yInput = Input.GetAxisRaw("Vertical");
    if (player != null && player.animator != null && rb != null)
    player.animator.SetFloat("yVelocity", rb.velocity.y);
    }

    public virtual void Exit() {
    player.animator.SetBool(animBoolName,false);

    }


    public virtual void AnimationFinishTrigger()
    {
    triggerCalled = true;
    }
    }


    如上代码,所有的类都分为 Enter、Update、Exit 三个阶段,而后续所有的类都继承自 PlayerState 类,所以所有状态都基于这三个过程进行。

3. 示例:playerIdleState

row
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//using UnityEngine.Playables;

public class PlayerIdleState : PlayerGroundedState
{
public PlayerIdleState(Player _player, PlayerStateMachine _playerStateMachine, string _animBoolName) : base(_player, _playerStateMachine,_animBoolName)
{

}

public override void Enter()
{
base.Enter();
//rb.velocity = new Vector2(0, 0);
player.ZeroVelocity();

}

public override void Exit()
{
base.Exit();
}

public override void Update()
{
base.Update();
if (xInput == player.facingDir && player.IsWallDetected())
return;
if (xInput != 0 && !player.isBusy)//不让玩家在攻击间移动
stateMachine.ChangeState(player.MoveState);
}
}


所有的继承类都具有 PlayerState 的构造函数和三个过程,基于这三个过程对玩家进行操作。