Twitter  Facebook  Google+  YouTube  E-Mail  RSS
The One Man MMO Project
The story of a lone developer's quest to build an online world :: MMO programming, design, and industry commentary
Class Based State Machine
By Robert Basler on 2015-04-26 23:00:36
Homepage: onemanmmo.com email:one at onemanmmo dot com

I've been using this for a while, but especially this week so I thought I'd share: it's a class-based state machine. State machines are usually big and messy and awful spaghetti code. Making states class-based makes a state machine cleaner and much easier to follow.

Here's the base classes for the state machine.


#ifndef STATE_H
#define STATE_H
//! brief State machine state base class. Each state is a class.
class State
{
public:
State( const char* const stateName )
:mStateName( stateName )
{
};
virtual void Enter( void ) {};
virtual void Leave( void ) {};
virtual const char* const Name( void ) { return mStateName; }
virtual ~State() {};
protected:
const char * const mStateName;
};
#endif

#ifndef STATEMACHINE_H
#define STATEMACHINE_H
#include "utility/state.h"
//! brief State machine base class. Each state is a class.
class StateMachine
{
public:
StateMachine( State *initState )
: mState( initState )
, mLastState( NULL )
{
};
StateMachine( void )
: mState( NULL )
, mLastState( NULL )
{
}
State* GetState( void ) const { return mState; }
const char * const GetStateName( void ) { return mState->Name(); }
virtual void SetState( State* newState )
{
delete mLastState;
mLastState = NULL;
if ( mState != NULL )
{
mState->Leave();
// Don't discard the previous state immediately so any ongoing operations
// can complete before the state is deleted.
mLastState = mState;
}
mState = newState;
if ( mState != NULL ) mState->Enter();
}
virtual ~StateMachine()
{
delete mLastState;
mLastState = NULL;
delete mState;
mState = NULL;
};
protected:
State *mState;
State *mLastState;
};
#endif
You might think to use constructors and destructors instead of Enter and Leave, but state machines usually change state while in a state so the state machine keeps the previous state around long enough not to crash. Enter() and Leave are also really good places to put trace output for debugging and Name() will give you the name of the current state. Sure would be nice if C++ had a __CLASS__ macro.

Here's a quick example. I typically put all the states in a single .h file (they're usually small.) I'll apologize if this doesn't compile, I just wrote it off the top of my head. The user only interacts with PingPongStateMachine, calling Ping and Pong when desired. The state machine takes two signals and has two states, ping and pong. Sending a ping to the machine in a ping state does nothing. Sending pong to the machine in a pong state does nothing. Sending ping to pong or pong to ping changes the state.


class PingPongStateMachine
: public StateMachine
{
public:
PingPongStateMachine( void ) {};
virtual void Ping( void )
{
static_cast< PingPongState* >( mState )->Ping();
};
virtual void Pong( void )
{
static_cast< PingPongState* >( mState )->Pong();
};
};

class PingPongState
: public State
{
public:
PingPongState( const char* const stateName, PingPongStateMachine *stateMachine )
: State( stateName )
, mStateMachine( stateMachine )
{
};
virtual void Ping( void ) = 0;
virtual void Pong( void ) = 0;
protected:
PingPongStateMachine* mStateMachine;
};

class PingState
: public PingPongState
{
public:
PingState( PingPongStateMachine *stateMachine )
: PingPongState( __FUNCTION__, stateMachine )
{
};
virtual void Pong( void )
{
mStateMachine->SetState( new PongState( mStateMachine ) );
};
};

class PongState
: public PingPongState
{
public:
PongState( PingPongStateMachine *stateMachine )
: PingPongState( __FUNCTION__, stateMachine )
{
};
virtual void Ping( void )
{
mStateMachine->SetState( new PingState( mStateMachine ) );
};
};

Anyway, I hope this makes your state machines less awful.

New Comment

Cookie Warning

We were unable to retrieve our session cookie from your web browser. If pressing F5 once to reload this page does not get rid of this message, please read this to learn more.

You will not be able to post until you resolve this problem.

Comment (You can use HTML, but please double-check web link URLs and HTML tags!)
Your Name
Homepage (optional, don't include http://)
Email (optional, but automatically spam protected so please do)
Type plane. (What's this?)

  Admin Log In



[The Imperial Realm :: Miranda] [Blog] [Gallery] [About]
Terms Of Use & Privacy Policy