In this post I want to show you how I like to go about creating a state machine. A state machine is usually used (or at least, I use it) for things like the game states (pause, playing, gameover,ecc) or AI for NPCs, like different enemy states (patrolling, chasing, shooting…).

This is a fairly standard method and we can achieve the same thing using interfaces. If you want to know how you can use interfaces to do this, I suggest you to take a look at this tutorial found on the Unity official website.

In this example I will show you some code of an hypothetical game controller which can run 3 states: playing, pause and gameover.

First thing, the Stateclass:

publicabstractclassState {
protectedstring stateName;
publicabstractvoid OnStateEnter ();
publicabstractvoid UpdateState ();
publicabstractvoid OnStateExit ();
publicstring getStateName()
{
return stateName;
}
}

The State class is an abstract class, which means we will not be able to create instances of it. This is because all the states we are going to write are going to be derived from this base class, which is only intended to be used as a parent and we will not need objects of its type.

The stringvariable stateNameis simply used to identify the state we are in in the console.

The 3 main methods used are also abstract methods. This implies that the classes which derive from this class MUST implement the methods body, which, in more complex projects, will most definitely behave in different ways.

Now that we have the base class ready, we can go on and write the 3 classes for our 3 states.

The pause state:

publicclassGameController_PauseState : State {
 GameObject obj;
 public GameController_PauseState(GameObject g,string name)
 {
  stateName = name;
  obj = g;
 }
 publicoverridevoid OnStateEnter ()
 {
  Debug.Log (obj.name + " enetered " + stateName); 
 }
 publicoverridevoid OnStateExit()
 {
  Debug.Log (obj.name + " exited " + stateName);
 }
 publicoverridevoid UpdateState ()
 {
  Debug.Log (obj.name + " is running " + stateName);
 }
}

The playing state:

publicclassGameController_PlayingState : State {
 GameObject obj;
 public GameController_PlayingState(GameObject g,string name)
 {
  stateName = name;
  obj = g;
 }
 publicoverridevoid OnStateEnter ()
 {
  Debug.Log (obj.name + " enetered " + stateName);
 }
 publicoverridevoid OnStateExit()
 {
  Debug.Log (obj.name + " exited " + stateName);
 }
 publicoverridevoid UpdateState ()
 {
  Debug.Log (obj.name + " is running " + stateName);
 }
}

The game over state:

publicclassGameController_GameOverState : State {
 GameObject obj;
 public GameController_GameOverState(GameObject g,string name)
 {
  stateName = name;
  obj = g;
 }
 publicoverridevoid OnStateEnter ()
 {
  Debug.Log (obj.name + " enetered " + stateName);
 }
 publicoverridevoid OnStateExit()
 {
  Debug.Log (obj.name + " exited " + stateName);
 }
 publicoverridevoid UpdateState ()
 {
  Debug.Log (obj.name + " is running " + stateName);
 }
}

You will notice that these three classes very much look alike, and that is because for this simple example these three states don’t do much more than displaying the name of the state that is acutally running. In a real game development environment you will want these classes to perform specific actions: for example, in the

OnStateEnter()

of the

GameController_PlayingState

class you will want to enable all the movement and input detection, whereas in the

GameController_GameOverState

you will do the exact opposite.

For each state I declared a GameObject variable, which is a reference to the game object which all these states are going to be part of. In our case, a GameControllerobject. I could have very well put that variable in the Stateclass.

The constructor is the same for each class, which simply gets passed the state name and the object reference.

Then, the three abstract methods are overridden. It is important to use the override keyword, and that is because an abstract method is implicitly a virtual method. If you omit the override keyword the compiler will start screaming at you.

Now the three state classes are written we can create the actual monobehaviour object script, which I called GameController.

publicclassGameController : MonoBehaviour {
 State currentState = null;
 GameController_PauseState pauseState;
 GameController_GameOverState gameoverState;
 GameController_PlayingState playingState;
 void Start () {
  pauseState = new GameController_PauseState(this.gameObject,"Pause State");
  gameoverState = new GameController_GameOverState(this.gameObject,"GameOver State");
  playingState = new GameController_PlayingState(this.gameObject,"Playing State");
  setState (playingState);
 } 
 void Update () { 
  currentState.UpdateState (); 
 }
 publicvoid setState(State newState)
 {
  if(currentState!=null)
   currentState.OnStateExit();
  currentState = newState;
  currentState.OnStateEnter ();
 }
 GameController_GameOverState getGameOverState()
 {
  return gameoverState;
 }
 GameController_PauseState getPauseState()
 {
  return pauseState;
 }
 GameController_PlayingState getPlayingState()
 {
  return playingState;
 }
}

Here we can see the beauty of inheritance and polymorphism.

We create a Statetype variable and we set it to null. Remember, we cannot instanciate objects of type State, but we can create variables of that type.

Then I create the 3 members of three different state types and, in the Start()method, I initialize them calling their respective constructor.

Next, the setState(…)method: this method accepts a Statetype variable which represents the states we wish to transit to. In its body, we first ensure the current state variable is not null, which is definitely going to be when the game starts as it has only been initialized to null.

Otherwise, if we are transiting from another state, the OnStateExit()of that particular state will be called.
After that, we simply assign the new state to the current state variable and call the OnStateEnter().
In the Update(), we simply call the UpdateState() method, which will run for which ever state object is contained in the currentState member.

This method is of course a public method so it can be called by other game objects in order to change the game state: for example, imagine your player being killed by an enemy while in the playing state. At this point you want to switch to the GameOverstate and display a game over screen. From the player script you can call setState(…) and pass it in its own variable gameoverState using the getGameOverState() method.

This is a pretty decent and organized way to create a state machine, which can be used for different purposes.

LEAVE A REPLY

Please enter your comment!
Please enter your name here