
My attempts at game programming usually turn into impenetrable spaghetti code: “If the player walks through this door, then have him talk to the princess, unless he’s killed a guard, in which case the guards attack, or if he comes out of the secret passage…”
The game I’m working on now is pretty simple, but I’ve kept it really clean (so far) by using a state machine to keep track of what should happen when. Basically, each scene in the game is a state. There’s an overarching state machine which runs the current state on each tap. A state can either return itself or choose a new state to run next time.
In Objective C (+cocos2d), a state looks like this:
@interface State : NSObject { CCLayer *ui; } -(id) init:(CCLayer*)layer; -(Class) processTouch:(UITouch*)touch withEvent:(UIEvent*)event; @end
The processTouch
function either returns nil
, which means “run me again next time” or the next state to run. The other half is a “machine” to run the states:
// ----------------- // Interface // ----------------- @interface StateMachine : NSObject { State *currentState; CCLayer *ui; } -(id) init:(CCLayer*)layer; -(BOOL) processTouch:(UITouch*)touch withEvent:(UIEvent*)event; @end // ----------------- // Implementation // ----------------- @implementation StateMachine // Initialize the state machine by setting currentState to the first state -(id) init:(CCLayer*)layer { self = [super init]; if (self) { ui = layer; currentState = [[FirstState alloc] init:ui]; } return self; } // Run this from the UI's touch dispatcher: it runs the current state's processing code -(BOOL) processTouch:(UITouch*)touch withEvent:(UIEvent*)event { Class nextState = [currentState processTouch:touch withEvent:event]; if (nextState != nil) { currentState = [(State*)[nextState alloc] init:ui]; } } @end
Then, you might have an implementation like this for a swords & sorcery RPG:
@interface PalaceDungeonState : State { Guard *guard; } @implementation PalaceDungeonState -(id) init:(CCLayer*)layer { // Use ui to render a dungeon } -(State*) processTouch:(UITouch*)touch withEvent:(UIEvent*)event { if (guard.alive) { [guard updatePosition]; } CGPoint touched = [ui convertTouchToNodeSpace:touch]; switch (touched) { case GUARD: [guard dies]; break; case STAIRWAY: return PalaceStairwayState; case SECRET_PASSAGE: return SecretPassageState; } return nil; } @end
I’m not thrilled with doing so much work in the init
, so for this type of game I’d probably move that to a start
method that would be called by StateMachine on state changes.
Regardless, I’ve found this makes it a lot easier to make a complicated sequence of events while keeping my code readable.
Interesting! Would you rather create an Arbitrary tree where each node represents a game state. Such node would have several children (representing several possible outcomes)?
LikeLike
I don’t think so, I’m trying to represent something that’s not a tree. I could represent it as a graph.
LikeLike