martes, 31 de mayo de 2011

GameEngine: Capitulo 2 . Graphics Manager

Hola a todos,

Bienvenidos a este segundo capitulo de como implementar un game engine y no volverte loco del todo en el intento....

Ayer hicimos la base general del proyecto, hoy implementaremos la base del GraphicsManager (o Graphic Engine como querais llamarle).

Estructurando el proyecto
Dado que a cada entrega (al menos ahora en estas primeras) tendremos que ir incluyendo archivos nuevos lo mejor que podemos hacer es estructurar el proyecto. Cada uno a su gusto, yo siempre lo que hago es : una carpeta "scr" donde pongo los .cpp y una carpeta "include" donde pongo los .h y a parte una carpeta "Data" donde pongo las imagenes, sonidos, modelos,etc... Cada uno a su gusto como más le plazca y se sienta más comodo para trabajar.

Una cosa importante es que os acordeis de incluir las rutas de la carpeta de includes en el proyecto, sino no os compilará .Para los que tengais mala memoria o no lo hagais hecho nunca, para incluir al proyecto la carpeta de include es tan sencillo como :
  1. Desde el VC++ click derecho encima del proyecto.
  2. Seleccionar propiedades
  3. En el menú de la izquierda seleccionar C/C++ y en el submenú seleccionar General
  4. En la primera opción de la derecha "Directorios de inclusión adicionales" poner la ruta de nuestra carpeta include.
  5. Compilar y comprobar que no da error.

Creando nuestra primera ventana gráfica
Está claro que nuestros juegos se tendrán que desarrollar dentro de una ventana gráfica debido a que, sin anímo de menospreciar la consola de DOS, ésta no es la más adecuada para desarrollar la visualización de nuestros videojuegos.

La creación de la ventana gráfica, por ahora, la realizaremos mediante GLUT. Ya que nos interesa que en cualquier momento podamos quitar GLUT y usar otro sistema para crear las ventanas , estas funcionalidades de pintado las englobaremos en una caja negra que llamaremos GraphicsManager (o Graphics Engine). Este modulo es el que se encargará en el futuro de realizar el pintado por pantalla de nuestras aplicaciones tanto 2D como 3D, así que será un código que estaremos modificando constantemente.

Para instalar GLUT en vuestro ordenador (si no lo teneis instalado) hay numerosas páginas que lo explicarán mucho mejor de lo que yo seria capaz de hacer. Una de ellas es la siguiente http://informatica.uv.es/iiguia/AIG/docs/tutorial.htm. Si no os funciona para vuestro caso concreto googlead un poco y vereis la solución rapidamente.

A fin de unir el código del Core de ayer con el Graphics Manager de hoy los cambios a realizar son mínimos. Os copio el código como quedaria. La parte que hace referencia a GLUT no le hagais mucho caso, miradla si quereis, pero no lo deis más importancia.

Core.h:
Respecto a lo que teniamos el otro dia incluid lo siguiente al final de la definición de la clase
private:
//Funciones del callback de glut
static void glut_idle();

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 "MyGL.h" //Includes standar para opengl,glut y compañia
#include "GraphicsManager.h" //Inclusión del motor gráfico

#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";
//Inicializamos el motor gráfico
GraphicsManager::singleton().Init(argc,argv);

//Asignamos todas las funciones de callback del glut
//a las funciones que definiremos posteriormente
glutIdleFunc(glut_idle);
}

//Funcion para ejecutar el bucle principal
void Core::Run()
{
//Pasamos el control del bucle principal al glut
//que nos gestionara los eventos y llamara a las
//funciones de callback.
glutMainLoop();
}

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


//CALLBACKS DE GLUT
//Esta funcion se llama cuando el sistema no hace nada (por ahora gestionada por glut)
void Core::glut_idle()
{
//Repintamos la escena
glutPostRedisplay();
}

MyGL.h: Este archivo, la verdad, no recuerdo de donde lo saqué, pero es muy práctico para todo el tema de opengl,glut,etc...
/**************************************************************************************************/
// 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
/**************************************************************************************************/

/**************************************************************************************************/
// MyGL.h : Includes para todo el tema de OpenGL
/**************************************************************************************************/

#ifndef __MyGL__
#define __MyGL__

#if defined(__APPLE__)
# include <Carbon/Carbon.h>
# include <OpenGL/OpenGL.h>
# include <GLUT/GLUT.h>
#elif defined(__linux__)
# include <GL/gl.h>
# include <GL/glut.h>
#elif defined(WINDOWS) || defined(_WIN32)
//# include <windows.h> // Header File For Windows//Se ha de quitar este include ya que si no genera errores con la raknet
# include <gl\gl.h> // Header File For The OpenGL32 Library
# include <gl\glu.h> // Header File For The GLu32 Library
# include <gl\glut.h> // Header File For The GLUT Library

#define GL_CLAMP_TO_EDGE 0x812F

#endif

#endif // __MyGL__

GraphicsManager.h (notar que también funciona con singletons como el core)
/**************************************************************************************************/
// 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
/**************************************************************************************************/

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

#ifndef __GraphicsManager__
#define __GraphicsManager__

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

public:
static GraphicsManager& singleton();

public:
//Funcion para inicializar el motor de gráficos
void Init(int* argc, char* argv[]);

//Funcion para desinicializar el motor de gráficos
void DeInit();
private:
//Funciones que no se llamarán desde nuestro código sino desde los callbacks de glut (por ahora)
//Funcion para pintar nuestros gráficos
static void Render();
//Función para reescalar los gráficos
static void Reshape();

private:
//Aqui irán los diferentes objetos que complementen el graphicsmanager
};

#endif

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

/**************************************************************************************************/
// GraphicsManager.cpp : Código del motor gráfico.
/**************************************************************************************************/

#include "GraphicsManager.h"
#include "MyGL.h"

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

using namespace std;

//Instancia única del graphics manager
GraphicsManager GraphicsManager::instance;

//Constructor
GraphicsManager::GraphicsManager()
{
}

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

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

//Funcion para inicializar el motor gráfico
void GraphicsManager::Init(int* argc, char* argv[])
{
cout << "Inicializamos el Graphics Manager\n";
//Creamos la ventana principal del programa utilizando la libreria GLUT
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(0, 0);
glutInitWindowSize(640, 340);
int mainWindow = glutCreateWindow("Lord Pakus Engine!!!"); //Viva la originalidad!!! :)
glutSetWindow(mainWindow);

//Asignamos todas las funciones gráficas de callback del glut
//a las funciones que definiremos posteriormente
glutDisplayFunc(Render);
// glutReshapeFunc(Reshape); //Por ahora la dejamos comentada por que no nos hace falta
}

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

//CALLBACKS DE GLUT, por ahora

//Funcion que se ejecuta automaticamente desde glut (por ahora)
void GraphicsManager::Render()
{
//AQUI IRÁ NUESTRA FUNCIÓN DE PINTADO. Por ahora vacia
}

//Funcion que se ejecuta automaticamente desde glut (por ahora). Por el momento la dejamos comentada por que no nos hace falta
//void GraphicsManager::Reshape()
//{
// //AQUI IRÁ NUESTRA FUNCIÓN DE REESCALADO. Por ahora vacia
//}



Eliminando la consola
Una vez ya tenemos nuestra ventana gráfica funcionando a más de uno de vosotros os sobrará totalmente la consola de DOS. Eliminarla es sencillo. Lo único que teneis que hacer es copiar esta linea...

//Delete console
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

..al inicio del main.cpp, justo debajo de los includes. Esta linea es la que le dirá al compilador que no cree la consola de DOS durante la ejecución de nuestro programa. Si en algún momento os hace falta para tracear o lo que sea, es tan sencillo como comentar la linea y volveréis a tener la consola.

Probando que todo funciona ok
La primera demostración que todo está ok es que os compile a la primera :). Una vez consigáis que os compile, si ejecutáis tendríais que ver algo de este estilo:


Como os dije en el capitulo anterior, por poco lo que parezca que avanzamos, al menos avanzamos. Ya llegará el momento (de aquí a no mucho) a poder ver cosas más impresionantes.
En breve os colgaré el código en un servidor de archivos para que os los podáis bajar directamente y no tengáis que ir haciendo copy-paste.

LordPakusBlog
Nos vemos, hasta el siguiente capítulo

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