Unity-FSM Finite State Machine

When we develop, to a certain extent, we will encounter dozens of states. If we continue to use Unity’s Animator controller, a large number of bool and float type variables will appear, and these intricate variables are connected to the Animatator controller like a maze version. The combination of lines will become extremely complex and cannot be well maintained and expanded. The occurrence of a BUG will cause developers to bear great mental stress during the development process. At this time, using finite state machines or AI behavior trees becomes an excellent solution. This article only records the development of finite state machines Using finite state machines for state management and switching can greatly reduce the difficulty of development. During the development process, you only need to pay attention to the switching between each state. That’s it

public interface IState
{
    //Enter state
    void Enter() { }
    //exit status
    void Exit() { }
    //Status logic update
    void LogicUpdate() { }
}
// <summary>
/// Hold all status classes, manage and switch them
/// Responsible for updating the current status
/// </summary>
public class StateMachine : MonoBehaviour
{
    IState currentState; //current state
    public Dictionary<System.Type, IState> stateTable; //Dictionary, used to save state and query state, convenient for state switching //Search
    public void Update()
    {
        currentState.LogicUpdate(); //Execute the logical switching function of each state, which can enable the state to detect //Transform, similar to the function in Update that receives information in real time
    }
    //switch state
    protected void SwitchOn(IState newState)
    {
        //The current state changes to the new state
        currentState = newState;
        //Enter new state
        currentState.Enter();
    }
    //
    public void SwitchState(IState newState)
    {
        //exit status
        currentState.Exit();
        //Enter new state
        SwitchOn(newState);
    }
    //Switch state function overload
    public void SwitchState(System.Type newStateType)
    {
        SwitchState(stateTable[newStateType]); //Pass in the state in the dictionary
    }

}
public class PlayerState : ScriptableObject, IState
{
    /******************Physical Detection****************/
    protected bool isGround; //Whether it is on the ground
    /*************basic information*************/
    protected bool isRun; //Whether to run
    protected bool isJump; //Whether to jump
    protected bool isIdle; //Whether it is still
    /******************Related components****************/
    protected Rigidbody2D my_Body2D; //Rigid body component, used to obtain the rigid body properties of the object
    protected Animator animator; //Animation component, used to play animations
    protected PlayerStateMachine stateMachine; //PlayerStateMachine, player state machine class, switches between execution states
    public void Initiatize(Animator animator, Rigidbody2D my_Body2D, PlayerStateMachine stateMachine)
    {//Get the animation, rigid body, and state machine classes passed in by PlayerStateMachine
        this.animator = animator;
        this.my_Body2D = my_Body2D;
        this.stateMachine = stateMachine;
    }
    public void PhysicalDetection(bool isGround)
    {
        this.isGround = isGround;
    }
    /// <summary>
    /// Status information transfer
    /// </summary>
    public void BasicInformation(bool isRun,bool isJump,bool isIdle)
    {
        this.isRun = isRun;
        this.isJump = isJump;
        this.isIdle = isIdle;
    }
    //Enter state
    public virtual void Enter() { }
    //leave state
    public virtual void Exit() { }
    //logic switching
    public virtual void LogicUpdate() { }
}
/// <summary>
/// Player state machine class
/// </summary>
public class PlayerStateMachine : StateMachine
{
    /******************************Detection information********************** *****/
    PlayerPhysicalDetection playerPhysicalDetection; //Physical detection component, this is inherited //MonoBehaviour, mounted on the player to detect //The player's physical information, such as whether it is on the ground
    /*************************status information********************** *****/
    //PlayerState resource file
    [SerializeField] PlayerState[] states;
    Animator animator; //Get animation components
    Rigidbody2D my_Body2D; //Get the rigid body component
    voidAwake()
    {
        /****************************Physical detection information********************** ******/
        playerPhysicalDetection=GetComponent<PlayerPhysicalDetection>(); //Get the physical detection component

        /****************************Status Information Component********************** ******/
        stateTable = new Dictionary<System.Type, IState>(states.Length); //Initialize dictionary
        animator = GetComponent<Animator>(); //Get animation component
        my_Body2D = GetComponent<Rigidbody2D>(); //Get the rigid body component
        //Iterator loop to get status
        foreach (PlayerState state in states)
        {
            state.Initiatize(animator, my_Body2D, this);//Pass in the animation component, rigid body component and PlayerStateMachine
            //Status stored in dictionary
            stateTable.Add(state.GetType(), state);
        }
    }
    private void Start()
    {//Execute Idle at the beginning and enter the Idle state
        SwitchOn(stateTable[typeof(PlayerState_Idle)]);
    }
    private new void Update()
    {
        base.Update();//Execute the Update function of the parent class StateMachine
        foreach (PlayerState state in states)
        {//Pass in the detection information
            state.PhysicalDetection(playerPhysicalDetection.isGround);
            state.BasicInformation( isRun, isJump, isIdle);
        }
    }
}
[CreateAssetMenu(menuName = "StateMachine/PlayerState/Idle", fileName = "PlayerState_Idle")]//Create file
public class PlayerState_Idle : PlayerState
{
    /************************Physical Detection************************/
    public override void Enter()
    {
        //Execute the state data file, first execute the entry state function, and then perform related behaviors in the entry state function
        //Enter the state and play the Idle animation by default
        animator.Play("PlayerIdle");
    }
    
    //Logic switching function, when a certain state is detected, the data file in that state will be executed immediately
    public override void LogicUpdate()
    {
        if(isRun)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Run)]);
        }
        if(isJump)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Jump)]);
        }
        if(my_Body2D.velocity.y<0 & amp; & amp;!isGround)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Fall)]);
        }
    }
}