«

»

State Machine Best Practices

In game development, state machines are the most common way to structure the behavior of code.  Wikipedia defines a state machine as :

state machine is a model of behavior composed of a finite number of states, transitions between those states, and actions.

Probably every game programmer has worked with or even created a state machine that turned into debugging mayhem. The road there is short and simple: Game programmers usually do not bother with planning a state machine carefully before starting to code. They usually just create an object, add the obviously needed states and add more states and functionality as needed over time. After a while, the code is full of “set state” calls mingling the states and creating a most complex structure, whose behaviour becomes almost impossible to predict.

We believe in the DRY principle propagated in the book “The Pragmatic Programmer” by Andrew Hunt and David Thomas. DRY is short for “Don’t Repeat Yourself”.  It simply means that duplication of information should be avoided. Creating a state-machine diagram and an implementation is an duplication of information already.

How can the DRY principle be applied to state machines?

Our answer is: Include the state-machine diagram in the code! Using such an in-code diagram makes it really easy to understand how an object works and how states are connected. This transparency is gained by triggering the state changes through observing changes in the internal data rather than using external stimuli. To make this possible we completely eliminated the use of the “set state” method (except for setting the initial state of an object).

Let’s get practical: We declare our state-machines in the constructor of our objects. First, we define all the needed states. States consist of a state method (reference to a member function) and a state id (constant integer value). Each state can have an arbitrary number of transitions. Each transition contains a reference to a transition-trigger method and a target-state id.  (A transition trigger returns true if a state-change condition is fulfilled, false if not.)

 1 // set up state- machine diagram
 2 m_sm = new cStatemachine(NUMBER_OF_STATES);
 3
 4 m_sm.AddState(ST_ANCHORING, StateAnchoring);
 5 m_sm.AddTransition(ST_ANCHORING, IsAnchorUp, ST_FLOATING);
 6
 7 m_sm.AddState(ST_FLOATING, StateFloating);
 8 m_sm.AddTransition(ST_FLOATING, IsSailOut, ST_SAILING);
 9 m_sm.AddTransition(ST_FLOATING, IsAnchorDown, ST_ANCHORING);
10
11 m_sm.AddState(ST_SAILING, StateSailing);
12 m_sm.AddTransition(ST_FLOATING, IsSailIn, ST_FLOATING);
13
14 // set initial state
15 m_statemachine.SetState(STATE_ANCHORING);

Let’s have a closer look at the example (Look here for a more extensive version): There you see the state machine of the object “Boat”. It starts in the state “STATE_ANCHORING” (line 15). As soon as the method “IsAnchorUp()” returns true, the corresponding transition kicks in and it changes the state to “STATE_FLOATING” (line 5). If the sailors should decide to lower the anchor again, the second transition of the floating state will cause the boat to return into “STATE_ANCHORING” (line 9) again. You are now surely able to easily understand what the other states do and how they interact.

Besides all the good things this approach brings into your project, it also has its downside: It is simply a bit of more work. The programmer has to think about the design of the state machine constantly while programming. It only works if you design your states well. In addition, a little bit more code is needed to implement a particular functionality, too.

Still, the benefits you can gain from this methodology are well worth it. This especially applies to a distributed multi-programmer environment like ours where you need to understand other people’s code fast.
Happy coding! :-)

Popularity: 5% [?]

2 comments

  1. Florian says:

    This is a very nice approach, it obviously helps to maintain the state-machine and keep a good overview.
    Besides the downsides you already mentioned, I see another issue with those auto-transitions: passing arguments from the current state to the next state.
    For a Player-Object in a 2d-jump-and-run we had ~20 states (in a 3d-environment, the player will certainly have a LOT more states).
    We had to choose between introducing more states that slightly differ from the already existing ones and to reuse the state-code but pass arguments to the next state to slightly change the state behaviour.
    I am sure you will agree the first option is not the way to go (it does not stick to the DRY principles) but it in the case of your state-machine, i don’t see another solution. Since the states are changed automatically, the current state does not have a chance to pass any arguments to the next one.
    What do you think?

  2. Manuel says:

    Thanks a lot for your thoughts! You are perfectly right that this approach may lead to problems when you have a huge number of states. Passing data from state to state is not considered, too.

    Well, you might get around this if you design your classes a bit differently. Maybe it is not such a good idea to have an object that has 20+ states especially if some states are similar. What you can do is extract functionality into subclasses. So you may have a player-movement class, a player behavior class and what else you may think of. This way you can re-use the the functionality in different states without violating the DRY principle.

    Well, keeping your objects small is in most cases a good idea anyway. It will give you more flexibility, while it may slow down your performance a bit though (depending on what system you are developing).

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">