« June 2004 | Inicio | August 2004 »

July 12, 2004

The State pattern and game development ( III )

So, it's time for some code.

You can download the code here. But you should take a look also at the linked list and hashmap we published some time ago, because we will need them now.

We have implemented four classes
Transition ( transitions )
State ( states )
SMachine ( state machine )
BEngine( behaviour engine, it's the one that manages all the game's state machines )

Things will work the following way: first we'll create the state machine that we are going to need, then we'll add it its states and transitions, and then we'll register that state machine in the global behaviour engine ( BEngine ). That behaviour engine will have a timer, so it will control the state machines execution.

First, we'll take a look at this engine. To simplify, it's just a list that holds a reference to each state machine that has been registered. So we can add new state machines to that list, remove a machine from it…

class BEngine { private var machineListVal: List; public function BEngine( ) { this.machineListVal= new List( ); } public function registerSMachine( machine: SMachine ) { this.machineListVal.push( machine ); } public function unregisterSMachine( machine: SMachine ) { this.machineListVal.deleteElement( machine ); } public function doProcess( ) { var it: IIterator= this.machineListVal.iterator( ); while( it.hasNext( ) ) { SMachine( it.next( ) ).executeCycle( ); } } }

The state machine is not a collection with all the states and transitions. However, we hold only a reference to the first state and the current state. Why? Because the states will be linked by the transitions, so we don't need to know all the states of the state machine, only the first one, and the current one ( so we can evaluate its transitions). However, we have to know if the machine is running, or is stopped.

class SMachine { . . . . public function resetToInit( initState: State ) { this.currStateVal = initState; this.initStateVal= initState; } . . . . }

Each state will hold a callback and an array with a reference to all the transitions that start at that state

class State { . . . . public function addTransition( transition: Transition ) { if( transitionDoNotExist( transition ) ) { this.transitions.push( transition ); } } . . . public function evalState( paramater: Object ): fgPair { var nextState: State= undefined; var retPair: Pair; var nTam: Number= this.transitions.length; for( var nIx: Number= 0; nIx< nTam && nextState== undefined; nIx++ ) { var currentTransition: Transition= this.transitions[ nIx ]; if( currentTransition.evaluate( ) ) { if( this.__traceInfo__ ) { trace( "TRANSIT:"+ this.id+ "->"+ currentTransition.endState.id+ " BY "+ currentTransition.id ); } nextState= currentTransition.endState; retPair = new fgPair( nextState, currentTransition ); } } return retPair; } public function execute( parameter: Object ): Object { if( this.__traceInfo__ ) { trace( "S[A]:"+ this.id+"; "+ this.actionCallbackVal.callbackMethod ); } return actionCallbackVal.fire( parameter ); } . . . . }

And last, the transitions. Each transition holds a reference to the initial and the final state and has the ability to execute a callback

class Transition { . . . public function set initState( state: State ) { state.addTransition( this ); this.initStateVal= state; } public function set endState( state: State ) { this.endStateVal= state; } . . . public function execute( parameter: Object ): Object { if( this.__traceInfo__ ) { trace( "T[A]:"+ this.id+"; "+ this.actionCallbackVal.callbackMethod ); } return this.actionCallbackVal.fire( parameter ); } public function evaluate( ): Boolean { var retVal: Boolean= true; if( this.evaluationCallbackVal!= undefined ) { retVal= Boolean( evaluationCallbackVal.fire( this ) ); } return retVal; } . . . }

Well, now it's time or an example. But that will be in another post.

July 07, 2004

The State pattern and game development ( II )

As we explained in the previous post, we’ll try to manage the object’s behaviour as a collection of related states, so the entity that implements that collection of states ( or state machine ) will be able to transit from one state to another. So we’ll have a classs ( Bubble ) that aggregates a collection of objects ( the states ) and a graphic to manage its presentation ( a movieclip ).

We are going to implement a non-hierarchical, non-concurrent state machine. If you want to read a deeper essay about state machines, I strongly recommend to take a look at FlashSim or quantum-leaps ( thanks to Jonathan Kaye for the links ). We will try to make things as simple as possible for the shale of performance ( remember, we are developing a game ).

First, here are two UML diagrams ( statechart diagrams ):

The bubble statechart

The game world statechart

Let’s take a closer look at the last diagram. There can be seen the different states that we’ve defined:

1.- initGame: General game initialization.
2.- initRound: General initialization of each game round. There will be setted the exploded bubbles counter, the time counter,…
3.- gamePlay. The user is playing the game.
4.- endOfTime: The time is over. We’ll check is the player has exploded enough bubbles.
5.- defeat. The player has not exploded bubbles enough. Ohhhhhhh.
6.- newChallenge. We offer the player the chance to play again, or to leave the game. If the player chooses to play again, we’ll move to initRound, and if the player chooses to leave the game, we’ll move to endOfGame.
7.- endOfGame. The game is over. We can send the player score to a server side script, …

Well, now that the concept has been explained we’ll try to explain how the state machine works.

First of all, when we enter a state an action is executed. In the most common state machine implementation there are three actions for each state, one will be executed when entering the state, another one when the state is executed, and another one when we leave the state. In this example, we’ll try to make everything as simple as possible, for the shake of performance, and we’ll execute just one action for each state.

Each arrow represents a transition. Each state contains a collection of transitions. This transitions can be callbacks that return a Boolean value, so the state machine will move by the transitions that returns a value of true. Usually, these transitions evaluate a given property of the class, and return true or false. Let’s explain it a bit more.

The state machine is closely tightened to a timer. So with every tick of the timer, some logic is executed ( all the transitions that start at the actual state are evaluated ), and state machine will move using the first transition that evaluates as true. So it will move to another state, where ( when the next tick occurs ) it will execute that state action. And next, it will evaluate more transitions, and…

Before taking a look at some code, we’ll try to explain it better going back to the Bubble class ( and diagram ). Let’s remember that this class is not a MovieClip subclass, but a complex entity that aggregates its state machine, and a movieclip.

So, when we create an instance of this class, and we create and start its state machine, will be in the first state: initBall. So, the state action ( initBallAction ) will be executed, and this action will attach a movieclip, and place it on stage. Then it will set a class flag ( isInitFlag, for instance ) to true, and it will set another flag to false ( isOutOfBoundsFlag ). With every timer tick, the transitions associated to this state will be evaluated ( here, there’s only one transition, that starts at initBall, and ends at moveBall ). So if that transitions returns the value of isInitFlag, it will return true, so the state machine will transit to the next state ( moveBall ).

This state has three transitions: the first one , that ends at itself, aonther one that ends at outOfBounds, and another one that ends at destroyBall.

So these transitions will evaluate ( in order ) the following flags: isOutOfBounds== false; isOutOfBounds == true and isClickedFlag == true.

When this state action is executed, first, we’ll calculate the next position of the bubble. Then, we’ll check if that position is out of the stage ( that includes bouncing against the floor ). If the bubble is out of stage, well set isOutOfBounds to true. So, if the bubble as not gone out of the stage, that flag will still be false. So, the transition that will return true will be the one that moves the state machine to the same state ( isOutOfBounds == false ), so well enter again the same state, so we’ll calculate the next ball position again,….

If the ball goes out of the stage, the transition that ends at outOfBounds will be the one to return true. So, the state machine will move to that state, where we’ll change the ball position or its velocity, and then we’ll return again to the moveBall state.

In the next post, we’ll see the code to implement all this, but now, the most important thing is to understand that is the entity itself the one that, just checking the value of some internal flags, changes it’s behaviour. And that that behaviour is separated in a collection of different objects.

There will be more soon.

July 05, 2004

The State pattern and game development ( I )

We are going to try to implement the State pattern to help us develop complex games or games whose entities have a complex or reactive behaviour.

First, we must consider that this implementation is quite lazy, and the example we're going to explain is simple enough to be explained, but with some complexity, so we can consider to implement this solution. Obviously, as the complexity of the game behaviour, or the complexity of the game entities behaviour grows, this will become a better solution. And also remember that my English is very poor, so, please, be nice :$

So, let's start. First, let's see what "state pattern" means. The intent is: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. ( here you can find a complete explanation )

So, this pattern should be a good solution when , for instance:
1.- The behaviour of an object depends of its internal state, and must change at runtime depending of that state.
2.- To describe the object's behaviour we use long conditional sentences, with multiple branches. We'll try to put each branch in a different object .

So, in other words, we will try to describe an object's behaviour as a collection of different states, in a way that the object itself could manage its behaviour moving from one state to another.

Sounds interesting, isn't it?. Let's try to build an example.

Now we need some imagination. Imagine we have to develop a game ( "Explode the bubble" ). There will be a given number of bubbles on the stage that bounce when they hit the ground, but that don't bounce against the walls ( I mean, if a bubble leaves the stage by its right side, it must appear again by the left side, and the opposite ). There's a given time to explode a given number of bubbles ( clicking them ). When a bubble explodes, another one appears, so there's always the same number of bubbles on stage. The initial position and velocity ( velocity as a vector, not speed ) are setted randomly. So, if we explode less than the minimum number of bubbles, we loose, but we explode more than that minimum number, we are offered to give it another try. Then we can leave the game, or try again, but we'll have to explode more bubbles in less time. And so on., until we loose, or we leave the game.

At first sight, we can separate our code into two different entities: the game world, and the bubble, so we could write two different classes. The first one will manage the top level logic ( putting bubbles on stage, checking if there have been enough exploded bubbles, checking if the time is over… ), and the second one will manage the bubble behaviour ( bounces, explosions,… ). Let's take a closer look at the last one.

Obviously, the graphic representation of the bubble will be a MovieClip. So we could think about the Bubble class as a subclass of MovieClip, where the onEnterFrame method will be responsible of checking if the ball has left the stage, if it must bounce against the floor, that places the ball on the stage depending of those circumstances ( in other words, that implements a mechanism to calculate the bubble's movement equation ), that detects when the user has clicked the ball, and tells it to the game world, that manages the explosion animation…..

Well, I guess this is not the moment to discuss about inheritance and composition ( I hope so, at least ). Anyway, I don't think there's a right way and a wrong way of doing things ( or of writing code ). But, in this case this approach doesn't seem to be the most appropriate.

First, our Bubble class will have to implement a wide open range of functionalities ( from physical behaviours to just graphics presentation ). So the cohesion will be very low. And that's not very good. ( in other words: keep complexity manageable ).

But also, the Bubble behaviour should be implemented using a long switch statement, or a lot of "if" sentences ( if it must bounce, make it bounce; if it leaves the stage, put the bubble in it's new position; if nothing else happens, just calculate its new position and place it ). And that's one of the scenarios to implement this pattern. And, not in this case, but if the entity's behaviour is reactive, everything makes even more sense.

Have we stimulated your curiosity?. There will be more soon.