State Machine Transitions

In my initial posting on state machines I necessarily skipped over a lot of detail that I’d like to start filling in. Let’s start by going back to this comment:

  // switch to stateNorthSouthGreen

In this state machine implementation, the current state is represented by a function, and that function is stored in the m_state member variable. Switching states is therefore almost as simple as just updating this variable. Let’s wrap it in a function with a helpful name:

function transition( newState:Function ):void {
// it's not quite this simple...
m_state = newState;
}

Recall that the dispatch() function simply forwards all events to the current state function. The new state function will receive all subsequently dispatched events. Continuing with our traffic light example, that means that CarEvents, for example, will continue to be dispatched, but now to our new state function.

It’s often useful to be able to do some additional work when entering or exiting a state. For example, a traffic light might might want to start a timer when entering a state which turns on the walk signal. This behavior can be added directly to the state functions by dispatching a couple of new events. Let’s define them first:

class StateMachineEvent extends Event {
public static const ENTER:String = "enter";
public static const EXIT:String = "exit";
...
}

Then we refine our implementation of transition():

function transition( newState:Function ):void {
m_state( new StateEvent( StateMachineEvent.EXIT ));
m_state = newState;
m_state( new StateEvent( StateMachineEvent.ENTER ));
}

(In other state machine models, individual states are sometimes modeled as classes. If you think of states that way, the enter and exit events are like the constructor and destructor, respectively.)

Finally, we add some code to our state function to handle these new events. I like to use the following skeleton for state functions:

function stateFunction( event:Event ):void {
switch( event.type ) {
case StateMachineEvent.ENTER:
// start Timers, etc.
break;
case ...:
// handle various other events
break;
case StateMachineEvent.EXIT:
// stop Timers, clean up , etc.
break;
default:
// Unexpected event! Maybe throw?
break;
}
}
}