Skip to main content

Hierarchical State Machine

Definition

todoThe state pattern allows objects to change behavior based on their internal state, effectively implementing finite state machines (FSM). This pattern is essential for managing AI behavior, player character states, game flow states, and animation controllers.

State machines prevent spaghetti code from proliferating conditional statements and ensure clear state transitions.

Complex AI and game flow often require hierarchical state machines (HSM) where states can contain sub-states.

Hierarchical state machines reduce code duplication by allowing states to share common behavior through parent states. When transitioning between child states, the parent state remains active and continues executing its logic.

Implementation

In GFF, a HSM is implemented by creating a class that derives from HierarchicalStateMachine<TState, TTransition>.

TState and TTransition are enum types. It is recommended to declare theses enums inside the HSM class since they are highly specific to its implementation.

The HSM structure must be declared before updating the it. It is recommended to do it in the constructor, then to call AssertCorrectness that verify the integrity of the HSM.

You can register a new state using the method RegisterState, then you register transitions to other states of the state machine with RegisterTransitionTo. You also can register a pass (a delegate) for any trigger type with RegisterTransitionTo.

Here are the available types:

    Begin: the pass is called when entering the state. Update: the pass is called each update while being in the state (after Begin the first time it enters the state, and before End the last time). End: the pass is called before leaving the state.

    Here is an implementation example:

    using Core;
    
    public partial class MyHSM : HierarchicalStateMachine<MyHSM.Id, MyHSM.Transition>
    {
        public AppState() : base(Id.Bootstrap)
        {
            this.RegisterState(Id.Initial)
                .RegisterTransitionTo(Transition.Next, Id.Main);
    
            {
                StateInfo main =
                this.RegisterState(Id.Main)
                    .RegisterPass(PassType.Begin, this.Main_Begin)
                    .RegisterPass(PassType.End, this.Main_End)
                    .RegisterTransitionTo(Transition.Next, Id.Final);
    
                outGame.RegisterState(Id.Main_SubState, defaultState: true)
                    .RegisterPass(PassType.End, this.Main_SubState_Update)
                    .RegisterTransitionTo(Transition.Toggle, Id.Main_AnotherSubState);
              
                outGame.RegisterState(Id.Main_AnotherSubState)
                    .RegisterPass(PassType.End, this.Main_AnotherSubState_Update)
                    .RegisterTransitionTo(Transition.Toggle, Id.Main_SubState);
            }
    
            this.RegisterState(Id.Final);
    
            this.AssertCorrectness();
        }
    
        private void Main_Begin()
        {
        }
      
        private void Main_End()
        {
        }
      
        private void Main_SubState_Update()
        {
        }
      
        private void Main_AnotherSubState_Update()
        {
        }
    
        public enum Id
        {
            Initial,
    
            Main,
            Main_SubState,
            Main_AnotherSubState,
    
            Final,
        }
    
        public enum Transition
        {
            Next,
            Toggle,
        }
    }
    drawing-4-1784721836.png

     

    Sources

    https://generalistprogrammer.com/game-design-patterns#state-pattern