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)]); } } }