Tutorial – Game State Machine – How/Why You Should Use Them

Like a lot of people, I first started programming because I liked video games. Of course, back when I was younger we made DOS games in QBasic that were nothing more than simple multiple choice text adventures. The code back then was a lot like this:

Print "A wild GOBLIN appears"
Print "(A)ttack. (R)un"
Input a$
if a$ = "a" GOTO 6
if a$ = "r" GOTO 7
Print "You ATTACK the GOBLIN"
Print "You RUN from the GOBLIN"

That may not be exactly how the code was written, but it’s been 20 years since I wrote in QBasic.

Lets examine this code for a moment:
Line 1 Prints a string to the screen
Line 2 Prints a string to the screen
Line 3 Records user input
Line 4 and 5 tell the application to do something based on the input supplied
Line 6 and 7 are the result of the input supplied

Games these days are not much different from this exact scheme. You give the user a situation and a means to work with that situation and then display the result of the player’s choice. It doesn’t matter if it’s a World of Warcraft boss or a wild GOBLIN appearing out of no-where. Now you can imagine writing something as complex as a WoW boss fight using QBasic would be less fun than swallowing glass, but the idea is the same.

pseudocode:

if(bossHealth >= 75.0f) {
    bossState = State.PhaseOne;
  } else if(bossHealth < 75.0f && bossHealth > 50.0f) {
    bossState = State.PhaseTwo;
  } else if(bossHealth < 50.0f && bossHealth > 0.0f) {
    bossState = State.PhaseThree;
  }

  if(bossState == State.PhaseOne) {
    // Hurl a fireball
  }

  if(bossState == State.PhaseTwo) {
    // AOE room with lava
  }

  if(bossState == State.PhaseOne) {
    // Hurl a fireball
    // AOE room with lava
    // Immune to taunt
  }

As you can see a boss fight can be made up of several different states, and the game can change depending on which state the boss is in. I used a very simple concept here just to explain what exactly a game state machine can do.

Lets take a look at a real example of a game state system. We will be using Unity3D and C# for this example but the idea should be portable enough to use whatever you are familiar with.

Lets first create a file called GameManager.cs. This file will contain all information relating to the game in general (game states, game wide variables, etc).

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

  public enum State {
    Playing,
    Paused
  }
}

Here we are creating a public enum called State and declaring the values Playing and Paused. Next we will need to create a constructor:
using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

  public enum State {
    Playing,
    Paused
  }

  public State state;
}

What this allows us to do is reference the different States in the enum from this or other scripts.

Lets initialize and set the first State of the game:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

  public enum State {
    Playing,
    Paused
  }

  public State state;

  void Awake () {
    state = State.Playing;
  }
}

What we are saying here is when this GameObject calls the Awake() function (which in Unity3D it always fires first), set the State to Playing.

Lets give some meaning to our states:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

  public enum State {
    Playing,
    Paused
  }

  public State state;

  void Awake () {
    state = State.Playing;
  }

  void Update () {
    if(Input.GetKeyDown(KeyCode.P) ) {
      if(state == State.Playing) {
        state = State.Paused;
      } else if(state == State.Paused) {
        state = State.Playing;
      }
    }
  }
}

Here we are saying that when the key “P” is pressed down (not held down), do something based on which State we are currently in. If we are Playing, then P will Pause. If Paused, then P will Play. Pretty straight forward and simple. Save the file and go back to the editor. Create an empty GameObject and give it the name and TAG of GameManager. Place the GameManager.cs script onto the GameManager GameObject.

Lets create a new C# script and name it TestStates.cs:

using UnityEngine;
using System.Collections;

public class TestStates : MonoBehaviour {

  public GameManager gameManager;

  void Awake () {
    gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
  }

  void Update () {
    if(gameManager.state == State.Playing) {
      PlayGame();
    } else if(gameManager.state == State.Paused) {
      PauseGame();
    }
  }

  void PlayGame () {
    Debug.Log("The Gamestate is currently: " + gameManager.state.ToString());
  }

  void PauseGame () {
    Debug.Log("The Gamestate is currently: " + gameManager.state.ToString());
  }
}

What this file is doing is creating a reference to the GameManager script by finding the object in the scene via the tag GameManager, every frame (Update() is called every frame) it is checking if the game state changes, if the game state changes call a function based on which state the game currently is in, function called will display a Debug.Log output showing the current value of the gameManager.state variable (which should be the current game state we are in).

You may say to yourself “Well, you can just use booleans to handle all of this”, which is true. Lets take a look at the same functions but using booleans instead of a state machine:
GameManager.cs:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

  public bool Playing;
  public bool Paused;

  void Awake () {
    Playing = true;
    Paused = false;
  }

  void Update () {
    if(Input.GetKeyDown(KeyCode.P) ) {
      if(Playing) {
        Paused = true;
        Playing = false;
      } else if(Paused) {
        Playing = true;
        Paused = false;
      }
    }
  }
}

TestStates.cs:
using UnityEngine;
using System.Collections;

public class TestStates : MonoBehaviour {

  public GameManager gameManager;

  void Awake () {
    gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
  }

  void Update () {
    if(gameManager.Playing) {
      Playing();
    } else if(gameManager.Paused) {
      Paused();
    }
  }

  void PlayGame () {
    Debug.Log("Is the game Playing? " + gameManager.Playing.ToString);
  }
  
  void PauseGame () {
    Debug.Log("Is the game Paused? " + gameManager.Paused.ToString());
  }
}

Now comparing these files together, you can see that using a state machine is much quicker to code and will end up being less complex down the road. The example I used is fairly simple, but when you get into functions that are really complex you don’t want to check boolean variables constantly. An example would be a character state:
Character.cs (Boolean version):
using UnityEngine;
using System.Collections;

public class Character : MonoBehaviour {

  public bool Idle;
  public bool Walk;
  public bool Run;
  public bool Sneak;

  private float moveSpeed;

  void Awake () {
    Idle = true;
    Walk = false;
    Run = false;
    Sneak = false;
  }

  void Update () {
    if(Idle) {
      Walk = false;
      Run = false;
      Sneak = false;
      moveSpeed = 0;
    } else if(Walk) {
      Idle = false;
      Run = false;
      Sneak = false;
      moveSpeed = 10;
    } else if(Run) {
      Idle = false;
      Walk = false;
      Sneak = false;
      moveSpeed = 15;
    } else if(Sneak) {
      Idle = false;
      Walk = false;
      Run = false;
      moveSpeed = 5;
    }

    HandleMovement(moveSpeed);
  }

  void HandleMovement(float speed) {
    rigidbody.velocity = new Vector3(speed, 0, 0);
  }
}

Character.cs (State Machine Version):
using UnityEngine;
using System.Collections;

public class Character : MonoBehaviour {

  public enum State {
    Idle,
    Walk,
    Run,
    Sneak
  }

  public State state;

  private float moveSpeed;

  void Awake () {
    state = State.Idle;
  }

  void Update () {
    if(state == State.Idle) {
      moveSpeed = 0;
    } else if(state == State.Walk) {
      moveSpeed = 10;
    } else if(state == State.Run) {
      moveSpeed = 15;
    } else if(state == State.Sneak) {
      moveSpeed = 5;
    }

    HandleMovement(moveSpeed);
  }

  void HandleMovement(float speed) {
    rigidbody.velocity = new Vector3(speed, 0, 0);
  }
}

A little more complex case shows how using states is a better idea. Less confusion and ideally more control over what your game is doing. You can also wrap entire functions within a state so they will never fire outside of the state you want them in, without the fear of missing a boolean check somewhere in the 5000 lines of code you just wrote.

Do yourself a favor and start using a state machine right now. It will save you a lot of time and hassle in the long run.

facebooktwittergoogle_plusredditpinterestlinkedinmail