miércoles, 22 de junio de 2011

GameEngine: Capitulo 11.StateManager - Modularizando el game engine (I)

Hola a todos

Bienvenidos a una nueva entrega de como crear tu propio game engine....

El capítulo de hoy, no os voy a engañar, es denso... tan denso que me he obligado a partirlo en dos capítulos, de no ser así me podrian acusar de intento de genocidio por la cantidad de lectores que de bien seguro moririan de coma cerebral al leerlo. :D

Dicho esto, el capitulo de hoy es la primera parte de la parametrización del game engine, es decir, de separar el código de game engine como tal, de el del juego. Más concretamente, esta primera parte tratará sobre la inclusión del StateManager que nos facilitará enormemente la comunicación juego-game engine.

Un StateManager no es más que un gestor de estados que nos permite , desde el juego, registrar estados que se ejecutarán en el game engine. Esto hace que el juego pueda pintar texturas y modelos o hacer sonar audio dentro de estos estados y el game engine sea capaz de gestionarlo.
He aquí el código del statemanager:

StateManager.h
/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// StateManager.h : Código del motor estados
// Caracteristicas especiales: Esta clase implementa un singleton, es decir, solo podrá existir un objeto de esta clase en todo el proyecto
/**************************************************************************************************/

#ifndef __StateManager__
#define __StateManager__

#include "State.h"

class StateManager
{
private:
State* m_current;
State* m_state[256]; //Podremos almacenar hasta 256 estados.
char identifier[256][256];

private:
// Constructor y destructor de la clase
static StateManager instance;
StateManager();
~StateManager();

public:
static StateManager& singleton();

public:
void init();
void registerState(char id[], State* state);
void changeTo(char id[]);
void update();
void draw();
};


#endif // __StateManager__

StateManager.cpp

/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// StateManager.h : Código del motor de estados.
// Caracteristicas especiales: Esta clase implementa un singleton, es decir, solo podrá existir un objeto de esta clase en todo el proyecto
/**************************************************************************************************/

#include "MyGL.h"
#include "StateManager.h"

#include <iostream> //Usada para imprimir por consola

using namespace std;

//Instancia única del StateManager
StateManager StateManager::instance;

//Devolvemos el puntero al singleton
StateManager& StateManager::singleton()
{
return instance;
}

//Constructor
StateManager::StateManager()
{
}

//Destructor
StateManager::~StateManager()
{
}

void StateManager::init()
{
m_current = NULL;
}

//Registramos un estado en concreto
void StateManager::registerState(char id[], State* state)
{
static int i = 0;

cout << "Registramos estado " << id << " con identificador " << i << "\n";
m_state[i]= state;
sprintf(identifier[i],id) ;

i++;
}
void StateManager::changeTo(char id[])
{
int i;
cout << "Queremos cambiar a estado " << id << "\n";

for(i = 0 ; i < 256 ; ++i)
{
if(!strcmp(id,identifier[i]))
{
cout << "Cambiamos a estado " << id << " con identificador " << i << "\n";
if(m_state[i] != NULL )
{
if(m_current != NULL)
m_current->leave(); //Salimos del estado viejo

m_current = m_state[i]; //Asignamos estado nuevo
m_current->enter(); //Entramos en estado nuevo
}
break;
}
}
}

void StateManager::update()
{
if( m_current != NULL )
m_current->update();
}

void StateManager::draw()
{
if( m_current != NULL )
m_current->draw();
}


Estos estados toman como base un clase abstracta llamada State.h

#ifndef __State__
#define __State__

class State
{
public:
virtual void enter() = 0;
virtual void leave() = 0;
virtual void update() = 0;
virtual void draw() = 0;

};

#endif // __State__

Aquí os dejo los ejemplos que usado para hacer la demo. Un estado de intro y uno de play.

//ARCHIVO DE JUEGO

#ifndef __IntroState__
#define __IntroState__

#include <ctime> //Funciones de tiempo

class IntroState
: public State
{
public:
IntroState() {}

public:

void enter()
{
}

void leave()
{
}
void update()
{
static clock_t timer = clock(); //Temporizador para ir a la pantalla de juego
if ( (clock() - timer) > 1000 )
{
Core::singleton().changeStateTo("PLAY");
}
}

void draw ()
{
Core::singleton().DrawSprite(10,10,"INTRO");
}
};

#endif // __IntroState__

//ARCHIVO DE JUEGO

#ifndef __PlayState__
#define __PlayState__

#include "MyGL.h"
#include <cstdlib>

class PlayState
: public State
{
public:
PlayState() {}

public:

void enter()
{
}

void leave()
{
}
void update()
{
//Nos sacamos de la manga que de vez en cuando haga sonidos aleatorios
if(!(rand()%100))
{
switch(rand()%5)
{
case 0 :
Core::singleton().PlaySound("SOUND_BOOST");
break;
case 1 :
Core::singleton().PlaySound("SOUND_PASOS");
break;

case 2 :
Core::singleton().PlaySound("SOUND_SALTO");
break;

case 3 :
Core::singleton().PlaySound("SOUND_COIN");
break;

case 4 :
Core::singleton().PlaySound("SOUND_DIE");
break;
}
}
}

void draw ()
{
//Parte 3D
glTranslatef(-1.5f,0.0f,-6.0f); // Move Left And Into The Screen

glPushMatrix();
glTranslatef(1.0f,-1.0f,1.0f);
glScalef(0.05f,0.05f,0.05f); //Hacemos que el modelo se vea un poco más pequeño.
Core::singleton().DrawModel("model"); //Pintamos el modelo que hemos cargado previamente.
glPopMatrix();

//Parte 2D
Core::singleton().DrawSprite(100,100,"TITULO");
}
};

#endif // __PlayState__

Si os fijaís en el código de los estados que implementamos dentro del juego, todas las funciones de pintado, audio, 3D ,etc... las hacemos pasar por la clase Core... es decir, la clase core es la única a la que haremos que el juego tenga acceso (aparte de la classe abstracta State), con esto lo tendremos todo controlado y no harán falta veintemil includes para que todo funcione.

El código de los cambios en el core y los diferentes managers que se ven afectados no los copio en parte por que no os daria información , en parte por que sería mucho código sin demasiado sentido. En la carpeta de megaupload colgaré este proyecto en breve...
http://www.megaupload.com/?f=K4XOGV3S aqui teneis el capitulo 10 y el capitulo 11 para que podais comparar las dos versiones.

Si compilais el código os dareis cuenta que lo único que hace es hacer sonar el sonido igual que antes, pero pinta solo el gráfico del ratón y los FPS. Ha dejado de pintar el modelo MD2 y el gráfico 2D. Esto es por que si bien el sonido puede ser ejecutado en cualquier momento los gráficos 2D/3D necesitan de una cierta preparación para ser pintados (seteo de perspectiva por ejemplo), de la cual ahora carecemos(ya que no pintamos dentro del graphics manager sino dentro de un estado de juego).

Este problema lo solucionaremos en el siguiente capitulo... no os preocupeis, que aunque largo, es fácil.

Espero que hayais aprendido y os haya servido de algo este capítulo.

Si teneis dudas no vacileis en preguntarlas.

LordPakusBlog
Nos vemos

3 comentarios :

  1. Ey pakus.. Gran tutorial.
    ¿No podrías resubir los sources?

    ResponderEliminar
    Respuestas
    1. No, por desgracia se me murió el PC donde lo tenía todo guardado...

      Pero bueno, la información está aquí, puedes recrear el proyecto a tu manera.

      Cualquier duda, ya lo sabes

      Eliminar
  2. No se si salio el comentario anterior pero gracias por su atención.
    A ver si lo analizo algo y puedo aplicar algo de lo que enseñas a un motorcito con SFML.

    Gracias y mucha suerte.

    ResponderEliminar

Entradas populares