lunes, 6 de junio de 2011

GameEngine: Capitulo 5. File Manager

Hola a todos,

Bienvenidos a una nueva entrega de como crear nuestro propio game engine y no perder (demasiado) nuestro atractivo hacia el sexo opuesto....

Este entrega se centra en eliminar los hardcodeos del módulo del audiomanager. La eliminación de estos hardcodeos solo puede realizarse mediante un fichero de configuración que contenga las rutas de carga de los sonidos y los tags que usaremos para hacer sonar los sonidos.

Esta funcionalidad la hemos encapsulado dentro del Filemanager (la implemenación que os paso está a medias, realmente seria y será mucho más extensa). Dicha clase se encargará de leer el archivo de configuración en cuestión e ir cargando los diferentes archivos de recursos que tenga nuestro juego. Todo esto hará que el sound manager (o cualquier otro modulo que use recursos) sea independiente del código para poder cargar sus archivos.

Se han creado los dos archivos (.h y .cpp ) del FileManager y se ha modificado el soundmanager para que use las nuevas funcionalidades. El core.cpp se ha modificado para usar las nuevas funciones a modo de muestra de funcionamiento.

FileManager.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
/**************************************************************************************************/

/**************************************************************************************************/
// FileManager.h : Código del gestor de archivos del game engine
/**************************************************************************************************/

#ifndef __FileManager__
#define __FileManager__

#define MAX_ELEMENTS_LOADED 256 //por ahora hardcodeado, lo arreglaremos en algún momento.

#include <stdio.h> //por ahora usamos esto... en el futuro lo cambiaremos por los streams de cpp

class FileManager
{
public:
FileManager();
~FileManager();

public:
//Funcion para inicializar el motor de sonido
void Init(char file[]);

//Funcion para desinicializar el motor de sonido
void DeInit();
//Lee una linea del archivo. Retorna true si existe la linea y false si no existe.
bool GetLine(char cad[]);

//SIN IMPLEMENTAR TODAVIA.
//Esta función se encarga de usar la función fptr para cargar un archivo con un cierto formato.
//La función de carga devolverá con que indice se ha cargado un cierto recurso que se le especificará mediante la cadena que se le pasará.
void Load( int (*fptr)(char *) );

//SIN IMPLEMENTAR TODAVIA.
//Esta función nos devuelve que número tiene asociado el recurso con el tag identificativo que le pasamos por parametro.
//El rendimiento de esta función es pésimo, pero por ahora no tiene más valor que el didáctico. En un futuro no demasiado lejano se implementará
//esta funcionalidad mediante un árbol.
int GetID(char cad[]);
private:
FILE *fp; //Fichero al cual encapsulamos
//char loaded[MAX_ELEMENTS_LOADED][256]; //Por ahora hardcodeado... tenemos un vector de strings (en el futuro se hará mediante un árbol de búsqueda)
};

#endif // __FileManager__

FileManager.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
/**************************************************************************************************/

/**************************************************************************************************/
// FileManager.h : Código del gestor de archivos del game engine
/**************************************************************************************************/
#include "FileManager.h"

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

using namespace std;


FileManager::FileManager()
{
}

FileManager::~FileManager()
{
}

//Inicialización del file manager
void FileManager::Init( char file[] )
{
cout << "Inicializamos el file manager del archivo " << file << "\n";
fp = fopen(file,"r");
}

void FileManager::DeInit()
{
cout << "Desinicializamos el file manager\n";
fclose(fp);
}

//Lee una linea del archivo. Retorna true si existe la linea y false si no existe.
bool FileManager::GetLine(char cad[])
{
//Si hemos llegado al final del archivo nos vamos
if( feof(fp) )
return false;

fscanf(fp,"%s\n",cad); //leemos la cadena y la devolvemos

cout << cad << "\n"; //Metemos la linea en el log

return true;
}

//Esta función se encarga de usar la función fptr para cargar un archivo con un cierto formato.
//La función de carga devolverá con que indice se ha cargado un cierto recurso que se le especificará mediante la cadena que se le pasará.
//Se presupone que los archivos de loading están formados por dos cadenas separadas por 1 igual (=, sin espacios), la primera que es un identificador y la segunda que es la ruta del recurso.
void FileManager::Load( int (*fptr)(char *) )
{
/* POR IMPLEMENTAR
char cad[256]; //Cadena donde guardaremos cada linea que leamos
char id[256]; //Cadena donde guardaremos el identificador de cada recurso
char ruta[256]; //Cadena donde guardaremos la ruta de cada recurso
int num_id; //número que nos asigna la función de carga del recurso.... un -1 significa que no lo ha podido cargar

//Leemos cada linea del archivo
while( GetLine(cad) )
{
sscanf(cad,"%s=%s",id,ruta); //Separamos las cadenas
num_id = fptr(ruta); //Cargamos el recurso

if (num_id == -1) //Si el recurso no se ha podido cargar, vamos a cargar el siguiente recurso
continue;

sprintf( loaded[ num_id ] ,"%s" , id ); //Nos guardamos la referencia al recurso
}
*/
}

//Esta función nos devuelve que número tiene asociado el recurso con el tag identificativo que le pasamos por parametro.
//El rendimiento de esta función es pésimo, pero por ahora no tiene más valor que el didáctico. En un futuro no demasiado lejano se implementará
//esta funcionalidad mediante un árbol.
int FileManager::GetID(char cad[])
{
/* POR IMPLEMENTAR
int i; //Iterador

for ( i = 0 ; i < MAX_ELEMENTS_LOADED ; ++i ) //Para cada elemento cargado
{
if( !strcmp(loaded[i],cad) ) //Hemos encontrado la cadena que nos interesa
{
return i; //Nos vamos devolviendo el valor que nos pedian
}
}
*/
return -1; //Si no encontramos lo que buscamos es que no está.
}

SoundManager.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
/**************************************************************************************************/

/**************************************************************************************************/
// SoundManager.h : Código de la parte de audio del game engine
// Caracteristicas especiales: Esta clase implementa un singleton, es decir, solo podrá existir un objeto de esta clase en todo el proyecto
/**************************************************************************************************/
#include "SoundManager.h"
#include "MyGL.h"

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

using namespace std;

//Instancia única del sound manager
SoundManager SoundManager::instance;

SoundManager::SoundManager()
{
}

SoundManager::~SoundManager()
{
}

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

//Sounds functions
void SoundManager::Init(int* argc, char* argv[])
{
char cad[256]; //Cadena donde guardaremos cada linea que leamos
char id[256]; //Cadena donde guardaremos el identificador de cada recurso
char ruta[256]; //Cadena donde guardaremos la ruta de cada recurso
int num_id; //número que nos asigna la función de carga del recurso.... un -1 significa que no lo ha podido cargar

cout << "Inicializamos el sound manager\n";

alutInit(argc, argv) ;

filemanager.Init("Data\\LoadSound.txt");


alGenBuffers(MAX_SOUNDS,buffer);

//Todo este código tendrá que ir al filemanager, pero por el momento lo dejamos aquí
//Leemos cada linea del archivo
while( filemanager.GetLine(cad) )
{
for( int i = 0 ; i < strlen(cad); ++i)
{
if ( cad[i] == '=' )
{
cad[i]='\0';
sprintf(id,"%s",cad);
sprintf(ruta,"%s",&cad[i+1]);
i = 257;
}
}

num_id = LoadSound(ruta); //Cargamos el recurso
cout << "Intentamos cargar sonido con ruta:" << ruta << "\n";

if (num_id == -1) //Si el recurso no se ha podido cargar, vamos a cargar el siguiente recurso
continue;

cout << "Sonido cargado con identificadores:" << id << " " << num_id << "\n";

sprintf( loaded[ num_id ] ,"%s" , id ); //Nos guardamos la referencia al recurso
}
alGenSources(MAX_SOUNDS, source);
}

void SoundManager::DeInit()
{
cout << "Desinicializamos el sound manager\n";
filemanager.DeInit();
alutExit();
}


//Carga el sonido cad y devuelve su posición en la que se ha guardado
int SoundManager::LoadSound( char cad[] )
{
//TODO : Evaluar los casos en los que el archivo no se puede cargar y devolver un -1 en consecuencia
static int s = 0;

SoundManager::buffer[s++] = alutCreateBufferFromFile (cad);
return (s-1);
}

void SoundManager::Play(int id)
{
static unsigned int s = 0;
alSourcei (source[s], AL_BUFFER, buffer[id]);
alSourcePlay (source[s]);

if(++s == MAX_SOUNDS)
s = 0;
}

void SoundManager::Play(char id[])
{
/*
int sound;
sound = filemanager.GetID(id);

if (sound == -1)
return;

Play( sound );
*/
int i; //Iterador

for ( i = 0 ; i < MAX_ELEMENTS_LOADED ; ++i ) //Para cada elemento cargado
{
if( !strcmp(loaded[i],id) ) //Hemos encontrado la cadena que nos interesa
{
Play( i ); //Hacemos el play del sonido en concreto
return;
}
}
}



Core.cpp

void Core::glut_idle()
{
//Nos sacamos de la manga que de vez en cuando haga sonidos aleatorios
if(!(rand()%100))
{
switch(rand()%5)
{
case 0 :
SoundManager::singleton().Play("SOUND_BOOST");
break;

case 1 :
SoundManager::singleton().Play("SOUND_PASOS");
break;

case 2 :
SoundManager::singleton().Play("SOUND_SALTO");
break;

case 3 :
SoundManager::singleton().Play("SOUND_COIN");
break;

case 4 :
SoundManager::singleton().Play("SOUND_DIE");
break;
}
}

//Repintamos la escena
glutPostRedisplay();
}

En cuanto a la entrega ejecutable es la misma que la del último dia, solamente que incluyendo el archivo Data\\LoadSounds.txt con la información necesaria para cargar el sonido.

Aunque el código no es ni mucho menos definitivo y es extremadamente mejorable, os ha de quedar claro el concepto que todo lo que se pueda sacar del código, mejor. Llegará el punto en el que recompilar el gamengine sea un par de minutos y en ese momento tal vez será demasiado tarde extraer la carga de recursos.

LordPakusBlog
Espero que os haya gustado, si teneis dudas, propuestas de mejores implementaciones o ampliaciones del módulo no dudeis en postearlas.. nos vemoss!!

9 comentarios :

  1. Hola :)

    Descargué el archivo del repositorio... y al ejecutar el ejecutable (valgame la redundancia xD) me marca este error:

    C:\Users\Nikolov\Desktop\Proyecto\Ejecutable\LPengine.exe

    No se pudo abrir la aplicación; la configuración en paralelo no es correcta. Consulte el registro de eventos de la aplicación o use la herramienta sxstrace.exe de la línea de comandos para obtener más detalles.

    Utilizo Windows 7... que puedo hacer? Compilarlo yo mismo?

    Veo que son varios archivos así que no tengo idea de como compilarlo aún... xD, apenas voy en Arreglos (en C)

    Y de IDE uso Code::Blocks

    Saludos :)

    ResponderEliminar
  2. Hola reethok...

    La verdad , este error no me suena de nada, yo también uso windows7 y nunca he tenido problemas.

    Lo más seguro es que al no haber instalado nunca el MSVC le falte alguna que otra libreria... mi recomendación, instalate o bien el paquete de redistribución de MSVC o bien directamente el MSVC versión express....(si no recuerdo mal es gratis)

    Para evitar este tipo de problemas ya intentaré crear un instalador, pero no aseguro nada.

    Tomatelo como unas prácticas de como hacer que las cosas funcionen :D

    Nos vemos

    ResponderEliminar
  3. Mmmm... ya bajé Visual C++ Express Edition y al leer las condiciones de uso me cabreé con lo monopolista que es Micro$oft... xD espero que la versión full no sea así ._.

    Y amm... al intentar compilar el capitulo 1 en VC++ me dió error... ya lo hice en Code::blocks y sí funcionó... tal vez es porque ya estoy acostumbrado a el y lo sé usar...

    Trataré de comiplar capítulo por capítulo, luego te digo que tal! :)

    Saludos! ^^

    ResponderEliminar
  4. Es lo mejor... ir compilando capítulo a capítulo... cualquier duda que tengas dilo y le buscaremos solución....

    ResponderEliminar
  5. Hola de nuevo Pako.
    Ya he llegado hasta aquí, pero no consigo compilar este capítulo porque en la línea 47 del fichero SoundManager.cpp tengo el objeto "filemanager" que no está declarado previamente.

    ResponderEliminar
  6. ¿No haría fata modificar el fichero SoundManager.h?

    ResponderEliminar
  7. Sip, evidentemente he cometido algún fallo. Te recomiendo que mires el código de alguna práctica más avanzada, que ahí seguro q está bien (creo que conque definas el objeto filemanager tienes suficiente)

    ResponderEliminar
  8. Pako, te propongo que hagas un capítulo esplicando cómo se linkan las bibliotecas, qué son los ficheros lib, h, dll. Así como las propiedades de los proyectos (casi todo el mundo usa MVS2010).

    ResponderEliminar
  9. Oído cocina...

    http://lordpakus.blogspot.com/2011/08/curso-de-programacion-capitulo-8.html

    Lo que no te guste o no lo veas claro, dilo y le buscaremos solución.

    ResponderEliminar

Entradas populares