El patrón State aplicado al desarrollo de juegos ( III )
Ya ha llegado el momento de empezar a ver código.
Puedes bajarte el código aquí. Nos apoyamos también en las implementaciones de lista enlazada y en la de HashMap
Hemos implementado la máquina de estados utilizando cuatro clases
:
Transition ( transiciones )
State ( estados )
SMachine ( mqáuina de estados )
BEngine( motor de comportamientos, es quien maneja las máquinas de estados )
Básicamente el procedimiento será el siguiente: se creará la máquina de estados de la entidad correspondiente, se le añadirán los estados y transiciones necesarias, y más tarde se registrará esa máquina en el motor global de la aplicación. Ese motor será el encargado de mandar a las máquinas de estados que tenga registradas que ejecuten el paso siguiente.
Veamos primero la estructura de ese motor. Básicamente no es más que una lista enlazada en la que guardamos todas las máquinas de estados que registremos, y métodos para registrar máquinas de estados, eliminarlas del registro, y darlas la orden de proceso.
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( );
}
}
}
La máquina de estados no la hemos implementado como una colección de estados y transiciones, sino que sólo guardamos una referencia al primer estado, y al estado actual en que se encuentre. Como ya las transiciones relacionan los estados entre sí, no tenemos necesidad de conocer todos los estados y transiciones desde la propia máquina de estados, sino que con conocer el primero es suficiente. Lo que sí que tiene que controlar esta clase es si la máquina de estados está arrancada, o parada
class SMachine
{
.
.
.
.
public function resetToInit( initState: State )
{
this.currStateVal = initState;
this.initStateVal= initState;
}
.
.
.
.
}
Cada estado tendrá asociado un callback ( que es el que va a ejecutar cuando la máquina de estados entre en ese estado, y un array con las transiciones que salen de él.
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 );
}
.
.
.
.
}
Y por fin, las transiciones. Cada transición tiene una referencia al estado inicial y al final, así como la capacidad de ejecutar el callback que se le pase.
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;
}
.
.
.
}
Pues ya sólo falta implementar un ejemplo de utilización. Pero eso será otro día.