domingo, 3 de julio de 2011

GameEngine: Capitulo 15.Mejorando el Graphics2DManager (III)

Hola a todos,

Bienvenidos a un nuevo capitulo de como programar un game engine desde 0...

El capitulo de hoy es una nueva mejora del manager de gráficos 2D esta vez centrada en la carga y pintado de gráficos empaquetados.

Habreís visto en muchas ocasiones que los juegos 2D utilizan los gráficos empaquetados, es decir, que muchos gráficos pequeños se agrupan en un solo gráfico que posteriormente se particiona. Las causas que estos gráficos se empaqueten pueden ser varias pero las tres más importantes son:
  1. Gestión de gráficos (es más facil gestionar un par de gráficos que no 30)
  2. Aprovechamiento del espacio debido a que las texturas deben ser cuadradas.(se pueden poner múltiples gráficos no cuadrados en una sola textura cuadrada)
  3. Menor tamaño ocupado (si en vez de 30 gráficos tenemos 1, nos ahorramos 29 cabeceras de archivo)

A nivel personal, en la mayoria de juegos que he visto este particionamiento se hace hardcodeado en código con todos los problemas que esto conlleva. Mi propuesta se basa en un archivo de configuración de particición que elimina totalmente los hardcodeos. Los cambios para implementar esta propuesta son numerosos pero se pueden resumir en el siguiente código:

Cambios en el Graphics2DManager:

typedef struct
{
int type;
float Xi,Yi,Xf,Yf; //Coordenadas de textura
float xi,yi,xf,yf; //Coordenadas de pintado
char cad[256];
}Task2D;

void Graphics2DManager::Draw(float Xi, float Yi, float Xf, float Yf,float xi, float yi, float xf, float yf,char cad[])
{
TextureManager::singleton().Texture(cad); //Usamos la textura
glBegin(GL_QUADS);
glTexCoord2f( Xf, Yi); glVertex2i( xf , yf );
glTexCoord2f( Xi, Yi); glVertex2i( xi , yf );
glTexCoord2f( Xi, Yf); glVertex2i( xi , yi );
glTexCoord2f( Xf, Yf); glVertex2i( xf , yi );
glEnd();
}

//Pinta un gráfico con su tamaño original en las coordenadas x e y
void Graphics2DManager::InsertDraw(float x, float y, char cad[])
{
int w,h;

w = TextureManager::singleton().GetWidth(cad);
h = TextureManager::singleton().GetHeight(cad);

task2D[num_task2D].type = TYPE_DRAW ;

task2D[num_task2D].Xi = 0.0;
task2D[num_task2D].Yi = 0.0;
task2D[num_task2D].Xf = 1.0;
task2D[num_task2D].Yf = 1.0;

task2D[num_task2D].xi = x;
task2D[num_task2D].yi = y;
task2D[num_task2D].xf = (x+w);
task2D[num_task2D].yf = (y+h);

sprintf(task2D[num_task2D].cad,"%s",cad);

num_task2D++; //Incrementamos el número de tarea
}

//Pinta un gráfico partido con su tamaño original en las coordenadas x e y
void Graphics2DManager::InsertDrawPart(float x, float y, char cad[], int p)
{
int w,h;
float xi,yi,xf,yf;

w = TextureManager::singleton().GetWidth(cad,p);
h = TextureManager::singleton().GetHeight(cad,p);

TextureManager::singleton().GetBox(cad,p, &xi,&yi,&xf,&yf);
//cout << "Pintamos gráfico partido " << cad << " componentes: " << xi << yi << xf << yf << "\n";

task2D[num_task2D].type = TYPE_DRAW ;

task2D[num_task2D].Xi = xi;
task2D[num_task2D].Yi = yi;
task2D[num_task2D].Xf = xf;
task2D[num_task2D].Yf = yf;

task2D[num_task2D].xi = x;
task2D[num_task2D].yi = y;
task2D[num_task2D].xf = (x+w);
task2D[num_task2D].yf = (y+h);

sprintf(task2D[num_task2D].cad,"%s",cad);

num_task2D++; //Incrementamos el número de tarea
}

void Graphics2DManager::Render()
{
char cadFPS[50];
static int x,y;
int i;

glLoadIdentity(); // Reset The View
switchToOrtho(); //Pasamos a 2D
glEnable(GL_TEXTURE_2D);

//Habilitamos el alpha
glAlphaFunc(GL_GREATER, 0.05f);
glEnable(GL_ALPHA_TEST);

//Pintamos el ratón
InputManager::singleton().DrawMouse();

//Renderizamos las tareas de pintado. Se tendrá que mejorar
for( i = 0 ; i < num_task2D ; ++i)
{
switch(task2D[i].type)
{
case TYPE_DRAW:
Draw( task2D[i].Xi , task2D[i].Yi , task2D[i].Xf , task2D[i].Yf,
task2D[i].xi , task2D[i].yi , task2D[i].xf , task2D[i].yf ,
task2D[i].cad ); //Pintamos la tarea que nos han encomendado
break;

case TYPE_TEXT:
Text( task2D[i].xi , task2D[i].yi , task2D[i].cad ); //Pintamos los textos que nos han encomendado
break;
}
}
num_task2D = 0; //Ya hemos pintado todas las tareas, así que no nos queda ninguna por gestionar.


//Pintamos FPS
sprintf(cadFPS,"FPS: %d",CalcFPS());
FontManager::Print(10,10,cadFPS);


glDisable(GL_TEXTURE_2D);

switchBackToFrustum(); //Pasamos a 3D
}

El código de juego se podría resumir en :

Core::singleton().DrawSprite(xbola,ybola,"PACK",1);       //Primer gráfico empaquetado
Core::singleton().DrawSprite(xpala1,ypala1,"PACK",2);   //Segundo gráfico empaquetado
Core::singleton().DrawSprite(xpala2,ypala2,"PACK",2);  //Segundo gráfico empaquetado

El código lo tenéis en la carpeta de descargas , esta vez no he hecho video por que sería exactamente igual al anterior solo hemos introducido un cambio en la estructura de archivos que aunque es muy beneficiosa, no aporta mejoras gráficas de cara al usuario.

LordPakusBlog
Este capitulo de por si no es gran cosa pero nos abre las puertas a módulos tan interesantes como el gestor de físicas, animaciones y de fuentes bitmapeadas, por solo contar con los más importantes. En breves capitulos os dareis cuenta de su importancia.
Espero que hayais disfrutado y aprendido.. nos vemos

0 comentarios :

Publicar un comentario

Entradas populares