I’m surprised how many people do not know what a state machine is or have never implemented one in Unity. Granted, a state machine is not required for many games and in fact not recommended for many real-time games (can you imagine how many states an RTS game would have?). However, states are interesting not just for game flow but for things such as simple AI.

Case in point, one of my first games I created in HTML5 was a space shooter featuring both enemy aliens and asteroids. Asteroids damaged both aliens and the player ship, and they spawned and initially moved in a random direction at the start of each game (there are a few checks to make sure they don’t spawn on top of the player or aliens). Alien AI, however, revolved around 4 major states:

  • Far from asteroids but close to player.
  • Close to asteroids but far from player.
  • Far from asteroids and far from player.
  • Close to asteroids and close to player.

I had an internal distance specified for what I considered ‘close’ (it was equal to how far the alien could shoot), while anything beyond the ‘close’ distance was considered ‘far’. The AI handling was a state machine that basically worked like this:

  • Far from asteroids but close to player: Shoot at player while moving away. The moving away part is important because I didn’t want the alien ships to seem ‘dumb’ and just crash into the player.
  • Close to asteroids but far from player: Shoot at closest asteroid while moving away from closest asteroid. This made the alien seem somewhat smart in navigating around and destroying asteroids that were close.
  • Far from asteroids and far from player: Fly toward player. There’s no point shooting anything if both asteroids and the player are outside of the alien’s range.
  • Close to asteroids and close to player: Shoot whatever is closest while moving away from whatever is closest.

The actual AI behavior is far from perfect as the aliens do crash with asteroids (and the player) from time to time due to changing direction while still maintaining a forward velocity toward an asteroid (or the player). Note that at all points in the game, each alien ship would fall into one of these four AI states until it is destroyed (ship destruction is not an AI state, but it is a state my game checked for).

Here’s a link to the game, but be warned it is incredibly unoptimized (there are audio issues on some browsers) and you may need to reload the page if you can’t play initially: https://www.zesix.com/html5/IcarusWA/

But let’s dive into how a simple state machine would be constructed in Unity using C#.

Firstly, if you haven’t worked with enumerations (enums for short) before, just know that they’re basically a name-denoted type. Suppose you want to store days of the week, then you could have an enum such as:

private enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

The reason why this is handy is because sometimes you want to refer to things by name instead of number but don’t want to mess with the complexity of an array or dictionary.

Let’s suppose I was to implement the three AI states mentioned earlier in Unity. I would start by making an enum declaration at the top of my Alien class, along with a variable to ‘hold’ the enum:

private enum AIState { CloserToPlayer, CloserToAsteroid, CloseToBoth, FarFromBoth };
private AIState currentState;

For the sake of simplicity, let’s assume ships are spawned at the start of the game (in my game they spawn in waves, but I won’t go into that sort of complexity in this post). In the Start() function for every class, you would assign a ‘default’ AI state:

void Start () {
    currentState = AIState.FarFromBoth;
}

I chose FarFromBoth because in this behavior, the AI will begin flying towards the player. Since I’m making a simple case here, let’s assume you already check the state of the alien ship every frame. Your Update() function would then need to be able to switch states, which is as easy as:

void Update () {
// Assume we already have code checking the AI state and setting it. 
// Refer to the code in the Start () function above on how to set the AI state.
    if (currentState == AIState.CloseToBoth) {
        BehaviorCloseToBoth();
    } else if (currentState == AIState.CloserToAsteroid) {
        BehaviorCloserToAsteroid();
    } else if (currentState == AIState.CloserToPlayer) {
        BehaviorCloserToPlayer();
    } else if (currentState == AIState.FarFromBoth) {
        BehaviorFarFromBoth();
    }
}

I’m also assuming there are functions for handling each of the AI states (the functions with ‘Behavior’ in their name) in the class somewhere. I’m not going to go into them for the purposes of keeping this post simple. Hopefully this post has been useful to someone.