lunes, 26 de septiembre de 2011

LPGameEngine: Capitulo 1 . SoundManager & ResourceManager

Hola a todos,

Este capítulo vuelve parcialmente obsoleto este otro e implementa lo que se dijo en su momento de un filemanager. Se puede aprender igualmente de él pero ya no cumple las condiciones de diseño actuales.

El cambio de este módulo proviene de dos factores:
- La gestión de recursos por un módulo centralizado (resource manager)
- La posibilidad de implementar varios managers de sonido de forma trivial

La forma de implementar estos cambios se ha realizado gracias al aprovechamiento de la herencia y las clases abstractas de C++. A grandes rasgos, para el que no sepa de que va el tema: herencia es que un clase deriva de otra (tiene características en común)  y clase abstracta significa que la clase madre no sirve para nada más que para tener subclases (de por si no tiene implementación).

Gráficamente podemos observar que la clase SoundManager es la madre de las diferentes clases que se encargan de gestionar el sonido:






Este tipo de estructura hace que en el Core.h no se defina un objeto soundmanager sino un puntero:  SoundManager*    pSound;        //Puntero al módulo gestor de sonido.

Dicho puntero podrá ser rellenado con una dirección a un objeto de cualquiera de los hijos de SoundManager... por ejemplo, dentro del resourceManager, se escoge que manager utilizar y se setea el puntero convenientemente:

void ResourceManager::GestSoundManager(void)
{
//Seteamos puntero

cout << "Sound Manager Driver : " << sound << "\n"; 
if( !sound.compare("NONE") )  {      cout << "SOUND:NONE\n"; Core::singleton().SetPointerSoundManager( NULL );      return;  } if( !sound.compare("OpenAL") )  {      cout << "SOUND:OpenAL\n";      Core::singleton().SetPointerSoundManager( (SoundManager *) new            SoundManager_OpenAL() ) ;      return; if( !sound.compare("FMOD") )          cout << "SOUND:FMOD\n";          Core::singleton().SetPointerSoundManager( (SoundManager *) new SoundManager_FMOD() ) ;          return; } if( !sound.compare("BASS") )          cout << "SOUND:BASS\n";         Core::singleton().SetPointerSoundManager( (SoundManager *) new SoundManager_BASS() ) ;          return;  } //AQUI IRÁN NUEVOS MANAGERS DE SONIDO SI HACEN FALTA. }
Posteriormente se podrá acceder al manager de sonido de esta manera sin importarnos que gestor de sónido tenemos por debajo:
psoundmanager = (SoundManager *) Core::singleton().GetPointerSoundManager();

    //Cargamos sonidos
    psoundmanager->PreLoad();

    //if (!LoadFile( soundroute , loadedSound , psoundmanager->Load ) )
    //    return false;

    TiXmlElement *root = doc.RootElement();
    for(TiXmlElement* element = root->FirstChildElement(); element; element = element->NextSiblingElement()) 
    {
        //Leemos el elemento
        id = element->FirstChild("ID")->FirstChild()->Value();            //tomamos el ID
        ruta = element->FirstChild("ROUTE")->FirstChild()->Value();    //tomamos la RUTA   
       
        //Cargamos el recurso
        loadedSound[id] = psoundmanager->Load(ruta);

        cout << "Sonido: " << id << ruta << "\n";
    }

    psoundmanager->PostLoad();

Ya que vemos este ejemplo hemos de comentar tambíen el gran cambio que se ha producido ahora y es que toda la gestión de los ficheros de recursos (configuraciones XML, audio, texturas, etc... ) pasarán por el resourcemanager que se encargará de gestionar dichos recursos.

La forma estandar de gestionar la carga será mediante la estructura
pmanager->PreLoad(); //Acciones a realizar antes de la carga (inicializaciones)
balbalballbal
pmanager->Load();  //Todo manager tendrá que tener una función de load de 1 solo recuros. El resource manager se encargará de cargar uno a uno todos los recursos.
balbalblablalb
pmanager->PostLoad(); //Acciones a realizar despues de la carga (desinicializaciones)

Todos los managers tendrán que utilizar esta estructura para poder interactuar con el resourcemanager.


Ya finalmente, por si os quereis animar a implementar FMOD y BASS os dejo la implementación con OpenAL para que veais que es trivial implementar nuevos módulos (el código completo lo podeis obtener del repositorio)

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

/**************************************************************************************************/
// Implementación del SoundManager con OpenAL
/**************************************************************************************************/
#include "SoundManager_OpenAL.h"

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

using namespace std;

SoundManager_OpenAL::SoundManager_OpenAL()
{
}

SoundManager_OpenAL::~SoundManager_OpenAL()
{
}

//Sounds functions
void SoundManager_OpenAL::Init(int* argc, char* argv[])
{
    cout << "Inicializamos el Sound Manager implementado con OpenAL\n";

    alutInit(argc, argv) ;

    num_element = 0;

}

void SoundManager_OpenAL::DeInit()
{
    cout << "Desinicializamos el Sound Manager implementado con OpenAL\n";
    alutExit();
}

//Todo aquello que se ha de hacer antes de cargar los sonidos
void SoundManager_OpenAL::PreLoad()
{
    cout << "Preparamos buffers de sonido\n";
    alGenBuffers(MAX_SOUNDS,buffer);
}

//Todo aquello que se ha de hacer después de cargar los sonidos
void SoundManager_OpenAL::PostLoad()
{
    cout << "Generamos fuentes de sonido\n";
    alGenSources(MAX_SOUNDS, source);
}

//Función para cargar un elemento con la ruta que se le indica
int SoundManager_OpenAL::Load(string route)
{
    buffer[num_element++]  = alutCreateBufferFromFile ((char *)route.c_str());

    if(num_element >= MAX_SOUNDS)
        return -1;

    return (num_element-1);
}

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

  if(++s == MAX_SOUNDS)
    s = 0;
}
Espero que os haya gustado y hayais disfrutado.

Nos vemos

LordPakusBlog

LPGameEngine: Capitulo 0 . Reescribiendo el motor

Hola a todos

Antes que nada, disculpad la tardanza en postear, pero entre el niño y los númerosos cambios que estamos teniendo en la estructura del motor de juego me ha sido imposible postear antes...

Si os fijais vereis que la etiqueta que he puesto es la de LPGameEngine (por contra de la que utilizaba hasta ahora de GameEngine) y que la numeración la he empezado desde el principio... NO, no me he vuelto loco, al menos no más de lo que ya estaba en su momento :D.

Este cambio es debido a que todo el motor lo estoy reescribiendo desde 0 (aprovechando todo lo que puedo de lo que ya tenia hecho) a fin de obtener un proyecto más mantenible. Había llegado a un punto en que me resultaba imposible incluir más código sin que me petara todo.

Coincidiendo con la reescritura del motor, killrazor ha empezado a colaborar con el furor de un adolescente hormonado (y eso que me consta que la adolescencia le queda lejos :D ) y ha propuesto numerosos cambios en la estructura organizativa del proyecto que me han parecido muy acertadas:
1. Tenemos página del googlecode: http://code.google.com/p/lpgameengine/ Es decir, tenemos repositorio donde dejar el código. No lo volveré a dejar en el megaupload con los numerosos problemas que había. Si quereis saber como bajaros el proyecto echadle un ojo aquí y postead pidiendo ayuda si teneís problemas
2. Tenemos grupo de googlegrouphs: http://groups.google.com/group/lpgameengine . El blog seguirá sirviendo para postear los tutoriales y las noticias y el grupo servirá para plantear los problemas y los dilemas del desarollo del engine. Hechadle un ojo, vereis que tenemos temas interesantes abiertos.

Como temas interesantes adicionales tenemos que usaremos la libreria BOOST para el desarollo del proyecto (a dia de hoy la encuesta es bastante favorable a su uso) que no es más que una especie de C++++ :). kill tiene tutoriales en su blog.

Los capitulos que vaya posteando bajo esta etiqueta serán los que aporten nueva información al proyecto. No me dedicaré a repostear lo que hice en su momento en el motor original.

Aparte de todo esto, el grupo de trabajo que estamos ahora nos dedicaremos a tener un motor funcional, esto quiere decir que habrá muchos flecos abiertos para que el resto de la comunidad pueda ir rellenandolos si quiere (nuevos managers, mejora de los existentes, "nuevos drivers")

Sin más, ahora os subiré el capitulo del sound mananger.

Espero que os guste, que aprendais y que querrais participar.

Nos vemos

LordPakusBlog

miércoles, 21 de septiembre de 2011

Curso de programación: Capítulo 12. Repositorios


Hola a todos...

Una cosa que en muchas ocasiones nos pilla desprevenidos en nuestro primer trabajo (se entiende que de programador) es el concepto de repositorio. A mi particularmente no me lo explicaron nunca en ninguna clase y tal vez es la cosa que más uso en mi dia a dia laboral.

Un repositorio es un lugar "virtual" donde guardamos gran cantidad de información susceptible de ser vista y tocada por mucha gente; en nuestro caso, proyectos de software. Es decir, vendria a ser algo así como un disco duro que está en un servidor donde copiamos nuestros archivos para que los demás los vean y de donde podemos bajarnos las cosas de las demás.

Hay muchos protocolos de repositorio (SVN,CVS,etc..) pero uno de los que más se usa  es el SVN. Para acceder a un repositorio se ha de tener un cliente de repositorio(un programa de gestión), nuevamente, hay muchos clientes, yo el que uso normalmente en casa es el TortoiseSVN . Vosotros usad el que queraís, hay muchos y lo importante al final es trabajar a gusto.

Vale, ok, ya se más o menos que es un repositorio y tengo el software para conectarme a el, y ahora que?. Pues nos conectamos al repositorio. Depende del cliente, pero normalmente para conectarte a un repostorio se ha de crear una carpeta vacia en nuestro disco duro , darle al botón derecho y darle a algo del tipo "CheckOut". al hacer esto nos pedirá la dirección del repositorio y usuario y password si es un repositorio restringido. Una vez hecho esto nos bajará el proyecto a nuestra carpeta.

Que acciones básicas se pueden realizar con el repositotio?
- CheckOut: Bajarte el proyecto del servidor a tu ordenador
- Update: Una vez el proyecto está bajado, podemos ir actualizando los últimos cambios que suben el resto de usuarios sin necesidad de bajar todo el proyecto.
- Commit: subir el proyecto al repositorio
- Compare: para comparar código del servidor con lo que tenemos
- revert: para eliminar nuestros cambios locales y quedarnos como al principio
- syncronizar: para gestionar multiples subidas desde diferentes ordenadores
- branch/tag : para generar diferentes versiones del proyecto
- switch: para cambiar de un versión a otra del proyecto

Estas acostumbran a ser las comunes, despues ya cada cliente puede montarselo de una manera u otra.


Que ventajas nos da usar un repositorio??
Si programas solo en casa, en proyectos de 1000 lineas de código, no te aconsejaria que usaras un repositorio, pero si trabajaras en un grupo de 50 personas distribuidas por todo el mundo con un código de 500k lineas , tu mismo verias que pasarse los cambios por mail no sería la mejor opción. :D
Aparte de la ventaja obvia de tener todo el código centralizado tenemos un conjunto de características que nos pueden resultar muy provechosas:
- Permisos: Hay una figura de administrador de proyecto que es el que da y quita permisos, esto quiere decir que no todo el mundo tiene por que poder hacer checkout ni mucho menos commit, reduciendo las subidas a un grupo de programadores experimentados (por ejemplo)
- Branches: el repositorio te permite crear diferentes versiones de tu código que pueden evolucionar por separado, por ejemplo : versión Linux , versión Windows , versión MAC y que haya diferentes grupos que trabajen en cada branch. Posteriormente estos branchs se pueden volver a juntar o no (por ejemplo, cuando es un branch de test)
- Comparación de código: Por defecto el repositorio guarda solamente las diferencias entre un commit y otro, esto significa que en todo momento se puede saber quien subió qué código , cuando y por que. si se detecta un fallo que apareció a partir de tal fecha se hace mucho más sencillo descubrir la raíz del problema.
- Revisión de código: La organización del repositorio está pensada para que la comunidad o un subgrupo de esta tenga que dar el visto bueno a un conjunto de cambios para que dichos cambios se hagan efectivos en la versión final. Esto en el fondo no es más que 100 ojos ven más que 2.

Ok, ya lo sé casi todo sobre repositorios, como me monto uno?? Pues la verdad, la mejor manera para mi gusto es lo que me recomendó killrazor en su momento : google code. No me meteré en como se gestiona, pero no es complicado y hay númerosos tutoriales por internet que lo explicarán mucho mejor que yo.

Finalmente, el resumen para los vagos que no quieren leerse toda esta parrafada :D
1. Bajate el tortoiseSVN
2. Crea un carpeta vacia en tu disco duro
3.Botón derecho SVN Checkout
4. Copia esto: https://lpgameengine.googlecode.com/svn/trunk/ donde te dice "URL of repository"
5. Dale al ok.

Si todo va bien se te tendría que bajar el proyecto, si no es así dimelo y lo miraremos.

Cualquier cosa que creais que falte, decidmelo y la incluiré o la explicaré de otra manera.

martes, 20 de septiembre de 2011

Game engine: The Next Generation :D

Hola a todos

Como supongo que más de uno estaria esperando la reestructuración del game engine va por buen camino.


Características que ya tenemos implementadas:
0. Tal y como kill me ha recomendado, estoy poniendo el proyecto en el repositorio svn del google code. El problema es que no me resulta demasiado intuitivo y me pierdo para subir todo el proyecto, solo me deja subir de archivo en archivo. Alguien sabe como hacerlo para subir todo el proyecto de golpe??

1. La clase core contiene todo el game engine (principalmente los managers)

2. Se ha creado el Resource Manager que es el encargado de tratar con todos los ficheros del game engine. A la espera de cierto código prometido por killrazor :D. De mientras usamos este módulo para cargar el archivo de configuración general del game engine. Aquí teneís un ejemplo de por donde irán los tiros:
<conf>
  <InputManager>Glut</InputManager>
  <SoundManager>OpenAL</SoundManager>
  <Graphics2DManager>OpenGL</Graphics2DManager>
  <Graphics3DManager>OpenGL</Graphics3DManager>
  <GraphicsASCIIManager>OpenGL</GraphicsASCIIManager>
</conf>

3. Se ha aumentado el nivel de exigencia del sistema. Si algo no funciona como debiera se aborta la carga del motor. Así evitamos problemas posteriores. Esto significa que todas las funciones de Init de los managers devolverán un bool diciendonos si han cargado bien o no.

4. Se ha facilitado mucho la posibilidad de  usar nuevas librerias para cosas ya hechas. Es decir, cambiar la libreria de sonido de OpenAL a BASS o FMOD es trivial. Esto se ha conseguido gracias a la herencia de C++. El core contiene un puntero generico a un manager (SoundManager por ejemplo) y el ResourceManager, en función de la configuración del game engine , se encargará de setearle un puntero a SoundManager_OpenAL o SoundManager_BASS por ejemplo. Mientras que las funciones coincidan en parametros y funcionamiento todo funcionará correctamente.

El código que hay por ahora del ResourceManager (solo es un ejemplo) es el siguiente
/**************************************************************************************************/
//        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
/**************************************************************************************************/

/**************************************************************************************************/
// ResourceManager.h : Código de la parte de recursos del game engine
/**************************************************************************************************/
#include "ResourceManager.h"

//Lista de managers
#include "Core.h"
#include "SoundManager_OpenAL.h"


#include "tinyxml.h"    //Usada para parsear XML

#include <iostream>        //Usada para imprimir por consola
#include <stdio.h>        //Usada par gestionar ficheros

using namespace std;

ResourceManager::ResourceManager()
{
}

ResourceManager::~ResourceManager()
{
}

//Nos devuelve si ha podido cargar adecuadamente o no.
bool ResourceManager::Init(char *route)
{
    cout << "Inicializamos el Resource manager\n";
   
    TiXmlDocument doc( route);

    //Si el archivo de configuración no existe, abortamos la carga
    if ( !doc.LoadFile() )
    {
        printf( "Could not load file %s. Error = %s. Exiting.\n", route,doc.ErrorDesc() );
        return false;
    }
    cout << "Cargamos el archivo de configuracion " << route << "\n";

    //Empezamos a leer el archivo por el principio
    TiXmlElement *root = doc.RootElement();

    //Cargamos los diferentes managers
    sound = root->FirstChild("SoundManager")->FirstChild()->Value() ;
    input = root->FirstChild("InputManager")->FirstChild()->Value() ;
    graphics2D = root->FirstChild("Graphics2DManager")->FirstChild()->Value() ;
    graphics3D = root->FirstChild("Graphics3DManager")->FirstChild()->Value() ;
    graphicsASCII = root->FirstChild("GraphicsASCIIManager")->FirstChild()->Value() ;

    //Gestionamos los managers
    GestManagers();

    //Si hemos llegado aquí es que hemos podido cargar todo adecuadamente.
    return true;
}

void ResourceManager::DeInit()
{
    cout << "Desinicializamos el Resource manager\n";
}



//Funciones internas del resourcemanager

//Nos dice si un fichero existe o no
bool ResourceManager::ExistFile(char *route)
{
    FILE *fp;

    fp = fopen(route,"r");

    if( fp )
    {
        fclose(fp);
        return true;
    }

    return false;
}

void ResourceManager::GestSoundManager(void)
{
    //Seteamos puntero

    cout << "Sound Manager Driver : " << sound << "\n";
    if( sound.compare("OpenAL") )
    {
        Core::singleton().SetPunteroSoundManager( (SoundManager *) new SoundManager_OpenAL() ) ;
        return;
    }

    //Aquí irian las lineas encargadas de setear los diferntes managers de sonido: ejemplo:
    /*
    if( sound.compare("FMOD") )
    {
        Core::singleton().SetPunteroSoundManager( (SoundManager *) new SoundManager_FMOD() ) ;
        return;
    }

    if( sound.compare("BASS") )
    {
        Core::singleton().SetPunteroSoundManager( (SoundManager *) new SoundManager_BASS() ) ;
        return;
    }
    */
}

void ResourceManager::GestInputManager(void)
{
    cout << "Input Manager Driver : " << input << "\n";
}

void ResourceManager::GestGraphicsManager(void)
{
    cout << "Graphics2D Manager Driver : " << graphics2D << "\n";
    cout << "Graphics3D Manager Driver : " << graphics3D << "\n";
    cout << "GraphicsASCII Manager Driver : " << graphicsASCII << "\n";
}

//Gestionamos la carga de managers
void ResourceManager::GestManagers()
{
    //Gestionamos cada uno de los managers
    GestSoundManager();
    GestInputManager();
    GestGraphicsManager();
}

Espero que os guste,

Nos vemos

LordPakusBlog

miércoles, 14 de septiembre de 2011

Relato: Ambientación de videojuego. Capitulo 8

"
- Escuchame atentamente hijo, tenemos poco tiempo. Cuando los soldados imperiales abran esa puerta yo moriré por la descompresión pero tu debes sobrevivir, has de saber que toda esta nave esta siendo atacada por ti y que no se irán sin ti, a menos que te defiendas...has sido creado para ello.

-Creado??? por el amor de los dioses papá, mamá y tu me adoptasteis, no me hicisteis

- Mamá y yo no fuimos más que los actores contratados para hacerte creer que tenias una vida normal, o al menos lo más normal que pudimos. Si del Comité hubiese dependido te habrías criado en un campo militar y serias poco más que un soldado cabreado.

- Pero, esto significa que...

- Significa que calles y escuches. Como ya sabes los dioses no són más que el resultado de la conciencia colectiva de todo grupo humano. Cuando más grande es el grupo y más sensible es su población más se expresa esta conciencia divina. El problema está en que el ser humano medio tiende a enloquecer ante la presencia divina...

- Si, ya lo se, Orbis Insanit y bla bla bla, al grano...a donde quieres llegar?

- Hace 30 años un grupo de investigadores , entre los que nos encontrabamos tu madre y yo,decidimos desarrollar un ser humano que fuera capaz de aguantar sin enloquecer la voz de los dioses, que fuera genéticamente compatible con todas las subrazas humanoides, que sus caracteristicas fisicas y psiquicas fueran dignas de epopeya. 5 años de trabajo agotador y muchas decepciones despúes naciste tu.

-.....

-Mejor así , no hables. Escuchame bien. Huye, salvate, como sea, pero huye. Cuando estés a salvo busca un transporte para llegar al planeta Rufus II y busca a un tal Azok. Es un viejo amigo mio y te ayudará, tal vez te podrá dar más respuestas de las que yo puedo darte ahora....

BOOM







....



- Y eso fue lo último que dijo mi padre en esta vida, los soldados imperiales reventaron la compuerta de nuestro camarote y la descompresión lo mató casi al instante...

- Y a tí como es que no te mató la descompresión?

- Aguanto bien la respiración.

- Bien, un momento. La escuadra mínima de soldados imperiales es de no menos de 20 soldados, como escapaste?

- Concretamente son 24 soldados. El último tuvo tiempo para avisar a los refuerzos y vino la escuadra de apoyo, 36 más.

- Como?? 60 soldados armados hasta los dientes, contando sargentos y cabos experimentados y sobreviviste? como narices hiciste para....

- Con mis manos. Eres tu Azok?? O tendré que seguir buscando a ese maldito chupasangres por muchos más sitios?

-Si...., soy yo. Sientete en tu casa. Es todo un honor tener al portavoz entre nosotros. Descansa y explicame más cosas de tu viaje.....
"

Espero que os haya gustado...

alguien se atreve a ir rellenando los espacios de la historia entre capitulo y capitulo??

Nos vemos

Entradas populares