Mostrando entradas con la etiqueta como crear un game engine. Mostrar todas las entradas
Mostrando entradas con la etiqueta como crear un game engine. Mostrar todas las entradas

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!!

lunes, 30 de mayo de 2011

GameEngine: Capitulo 1. Core Manager

Hola a todos...

He aquí la primera entrega "Como crear tu propio motor de videojuegos y no morir en el intento".

Antes que nada hay que dejar un par de puntos muy claros:
  1. Para mi es un proyecto vital, es decir, me podré estar toda la vida haciendolo y esto es un hecho que asumo debido a que quiero aprender, no a acabar un producto en un tiempo determinado. Lo digo por que en muchas ocasiones existe el fenomeno "esto lo acabo en 1 semana" muy propio de los novatines, que acostumbra a acabar en tragedia y lloros (y como daño colateral, abandonar el proyecto)
  2. En la medida de lo posible no usaré librerias de terceros (aunque si API's y algún que otro código suelto). Esto es debido a que por un lado no aprenderiamos (que entiendo que es nuestro objetivo principal) y por otro lado siempre podemos tener sorpresas desagradables con las licencias. No obstante, que yo no use librerias no significa que vosotros no podais, la idea es que el diseño del motor sea lo suficientemente solido y modular como para aceptar cambios de bloques enteros sin modificar la funcionalidad total.

Una vez dicho esto, pongamonos a trabajar.

El motor lo implementaré en C++ y aquí si que hay poca discusión. Soy un enamorado rematado de C y de ASM, pero en cuanto a modularidad C++ no tiene comparación. Como siempre, sois libres de montarlo como querais. Con lo que yo muestre por aquí no deberia haber problema en implementar el motor en Java(por ejemplo)..... aunque, quien en su sano juicio querría hacer eso? :D

Mi entorno de desarrollo es Windows y utilizo VisualStudio. Nuevamente, hacedlo en el compilador que querais ( DevCpp, gcc sobre Linux,etc... ), yo lo mostraré en el que tengo. Si no teneis ninguno en especial creo recordar que existe una versión Express del Visual Studio de gratix...

Como crear el proyecto:
Ahora que está todo dicho, crearemos el proyecto:
  1. Abrís el Visual Studio y creais un nuevo proyecto.
  2. Win32 >> Aplicación de consola Win32
  3. Siguiente
  4. Marcad "Aplicación de consola" y "Proyecto vacío"
  1. Finalizar

Las primeras lineas de código:

Ahora tendremos un proyecto mondo y lirondo, absolutamente sin ningun archivo.

Como ya supondreis, lo primero que haremos será crear un archivo que llamaremos main.cpp y otros dos a los que llamaremos Core.h y Core.cpp.

Copiad el siguiente código en los siguiente archivos:

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

/**************************************************************************************************/
// main.cpp : Main de nuestro gameEngine
/**************************************************************************************************/

#include "Core.h"

int main (int argc, char* argv[])
{
//Inicializamos la clase Core con los argumentos del programa
Core::singleton().Init(&argc, argv);

//Empezamos el programa dandole el control al bucle principal de la clase core.
Core::singleton().Run();

//Desinicializamos la clase Core
Core::singleton().DeInit();
}


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

/**************************************************************************************************/
// Core.h : Interface del core manager.
// Caracteristicas especiales: Esta clase implementa un singleton, es decir, solo podrá existir un objeto de esta clase en todo el proyecto
/**************************************************************************************************/

#ifndef __Core__
#define __Core__

class Core
{
private:
// Constructor y destructor de la clase
static Core instance;
Core();
~Core();

public:
static Core& singleton();

public:
//Funcion para inicializar el bucle principal
void Init(int* argc, char* argv[]);

//Funcion para ejecutar el bucle principal
void Run();
//Funcion para desinicializar el bucle principal
void DeInit();
private:
};

#endif


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

/**************************************************************************************************/
// Core.cpp : Código del core manager.
/**************************************************************************************************/

#include "Core.h"

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

using namespace std;

//Instancia única del core manager
Core Core::instance;

//Constructor
Core::Core()
{
}

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

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

//Funcion para inicializar el bucle principal
void Core::Init(int* argc, char* argv[])
{
cout << "Inicializamos el Core Manager\n";
}

//Funcion para ejecutar el bucle principal
void Core::Run()
{
//Variable usada de iterador
int i = 0;
//Bucle fool para comprovar el funcionamiento de la clase core...
for( i = 0 ; i < 1000000; ++i)
{
if(!(i%10000))
cout << "Ejecutamos el paso de core numero: " << (i/10000) << "\n";
}

}

//Funcion para desinicializar el bucle principal
void Core::DeInit()
{
cout << "Desinicializamos el Core Manager\n";
}


Ahora compilad (no os deberia dar ningún error) Como los más avispados ya supondréis si ejecutáis el exe resultante en una ventana de cmd el resultado que debereis ver será:

Inicializamos el Core Manager
Ejecutamos el paso de core numero: 0
Ejecutamos el paso de core numero: 1
Ejecutamos el paso de core numero: 2
.
Ejecutamos el paso de core numero: 98
Ejecutamos el paso de core numero: 99
Desinicializamos el Core Manager

Y por hoy hasta aquí llegamos, tal vez os parezca poco, pero sin lo que se ha explicado no se podría desarrollar el resto del proyecto. Si habéis tenido problemas para entender el código, googlead, es imprescindible entender este código a la perfección debido a que es muy sencillo y la complejidad no va ha hacer más que incrementarse. Y bueno, dudas y sugerencias, para esto teneis el blog.

LordPakusBlog
Nos vemos, hasta el siguiente capítulo.

Entradas populares