A One-Script State Machine for Unity

12-20-23


When I was first learning the state machine pattern I followed a tutorial. In this tutorial, the state machine was set up such that there was one class (one script) per state. It added up to a lot of individual scripts.


Now, I am glad the tutorial showed this structure because…


However, as I continued my project and got more comfortable, I wanted to try to deviate from that template. One of my project goals was to implement a boss character. I felt that making 8 scripts just to control one simple boss character’s behavior was overkill. The boss wasn’t going to require complicated logic! He was just meant to walk towards the player and attack them. I wanted all of his logic to live inside of one easy-to-manage script.


I still wanted these features of state machines:


I also really wanted my code to be readable because I am not a good programmer, motivation is a limited resource, and I knew the less hassle it was to read my code the easier it would be to persist with the project.


Implementation

First, define each of the possible boss states in an enum:


public enum SIMPLESTATE { Walk, Attack }


Then — use Update as the “state machine” to check if we should change state this frame, change state if needed, and run the appropriate state function every frame.


void Update()
{
    // very simple state machine!

    // check if we met any exit conditions this frame -- if so, change our current state to a new state.
    if (exitConditionsMet)
    {
        currentState = nextState;

        // reset some crucial tracking values correctly as we enter a new state.
        isEntryFrame = true;
        exitConditionsMet = false;
    }

    // now, actually execute the state behavior based on whatever our current state is.
    if (currentState == SIMPLESTATE.Walk)
        Walk();
    else if (currentState == SIMPLESTATE.Attack)
        Attack();
}

Then, create one state function for each state. The state function template looks like this:

{
    // ENTRY
    if (isEntryFrame)
    {
        // put behavior here that you want to happen the first frame you enter this state

        // reset timers and tracking variables
        timeInState = 0f;
        isEntryFrame = false;
    }

    // UPDATE
    // put behavior here that you want to repeat every frame you're in this state


    // EXIT
    // check if the exit conditions were met this frame
    exitConditionsMet = ([this state's exit conditions]);
    if (exitConditionsMet)
    {
        // put behavior here that you want to happen right before you leave this state
        // then, set the next state.
        nextState = SIMPLESTATE.OtherState;
    }
}


This state has an entry, an update, and an exit. You can add whatever behaviors you like in each part!


Here’s an example of how an actual state — not just a template — could look:

{
    // ENTRY
    if (isEntryFrame)
    {
        //on entry, trigger the walk animation
        SetBodyAnimation(walk, true, 1f);

        //reset timers and tracking variables
        timeInState = 0f;
        isEntryFrame = false;
    }

    // UPDATE
    // update the timer tracking how long we've been in this state
    timeInState += Time.deltaTime;
    // each frame we are in this state, try to play the walk animation, and move the character.
    SetBodyAnimation(walk, true, 1f);    
    Move(1f);
    

    // EXIT
    // check if we've been in the state long enough to switch to another one
    exitConditionsMet = (timeInState >= desiredTimeInWalkState);
    if (exitConditionsMet)
    {
        nextState = SIMPLESTATE.Attack;
    }
}


A Note about Spine Animations

One of the challenges of this project so far has been figuring out how to use Spine animations with an animation controller!

Several of the tutorials I’ve found on creating a character with states, use Unity’s built in Mecanim system to build a node graph representing all the behavior logic for a boss, and then play standard animation clips inside the Mecanim system.


This IS possible with Spine. When you export Spine assets, you can choose between 2 formats — SkeletonMecanim and SkeletonAnimation. Which one you choose seems to have a lot to do with your preferred workflow. To cite this forum post breakdown by Pharan:

SkeletonMecanim

– Good if you like working with Mecanim (and all its quirks)

– Can use blendtrees?


SkeletonAnimation

– Good for simple stuff.

– More flexible if you want to code your own animation controller.

– Apparently “events have better performance. (Spine uses native C# events. Unity uses slow reflection for its SendMessage-based animation events.)” (ew reflection)


I tried the Mecanim approach but — since I’m NOT familiar with Mecanim– and at the time didn’t know about blend trees or animation layers — trying to express that behavior in the Mecanim Animator made me feel like I was walking down a path to spaghetti hell. I was daunted at the task of representing our state changes (how many arrows does it take to set up 8-directional aiming?!) this way. I was also worried the resulting node graph would be unreadable.


What the graph looks like when you’re bad at mecanim


For that reason, I chose to use SkeletonAnimation and to create code animation controllers to play the animation clips. This is also why I really wanted a simple, one-script animation controller that I could easily set up for each boss. : )

This may change though if the artist I am working with wants to use Mecanim instead. Ease of use and artist/designer accessibility is important!


There is an interesting looking setup by Majicpanda that uses SkeletonAnimation inside of a Mecanim. I might try to emulate this later down the line.


Full script is on Github: LINK



As always, I’m a noob so suggestions/corrections welcome 🙂 Thanks for reading!