Como se hacen los Juegos en C++

Si me preguntaran sobre cual es el mejor lenguaje para programar videojuegos, diría que depende. Depende del próposito, plataforma, tipo de juego y un sin fin de detalles más. Pero si me dijeran que dijera el más general, el más usado, el más potente y en el que estén hechos la mayoría de las superproducciones de las grandes compañías diría C++ sin pensármelo dos veces. C++ es el lenguaje por excelencia en el mundo de los videojuegos debido a que combina el bajo nivel y la eficiencia que da C con la programación orientada a objetos que es casi imprescindible en la programación de videojuegos.

El problema es que para los recién llegados al mundo de la programación empiezan a investigar y mucha gente les mete miedo diciendo que C++ es muy complejo y que no es un buen lenguaje para comenzar, esto es una verdad a medias, es cierto que hay lenguajes de más alto nivel que simplifican ciertas cosas a cambio de sacrificar control y eficiencia, pero por lo menos en mi caso eso es mucho peor para aprender. Me explico, cuando uno está aprendiendo tienen que entender el porqué de las cosas, cual es la finalidad de esto o lo otro, porque mejor usar este tipo de dato o este otro. Eso con lenguajes de alto nivel se pierde, te abstraen tanto del hardware y el bajo nivel que no comprendes porque se usan ciertas cosas. Se aprende “porque sí” y no se está aprendiendo realmente como funcionan las cosas. 

Con C++ tienes que comprender que es lo que está haciendo el ordenador y porque las cosas se hacen así. Yo sinceramente aprendo mejor las cosas cuando entiendo el porqué que cuando de me dicen: “simplemente es así”. Así que animo a todos los nuevos programadores a no tener miedo y a empezar a programar con C++ y a entender las cosas. Muchas veces nos dicen que son complejas, pero no es cierto al fin y al cabo la programación es sencilla, se complica cuando se empieza a abstraer y abstraer, pero de eso ya habrá tiempo.

Otro de los grandes escollos una vez se decide empezar con C++ es el temido “¿Por dónde empezar” muchos potenciales programadores se desaniman al poco tiempo porque no encuentran algo que les enseñe desde cero o se desmotivan viendo que tienen simples programas de consola, ¡Ellos quería programar videojuegos, no letras blancas sobre un fondo negro! Sobre esto último no puedo ayudar mucho, es la única forma de empezar, es muy difícil aprender conceptos básicos con aplicaciones gráficas. Sobre lo primero voy a intentar ayudar sobre el cámino que yo seguiría.

En la red hay mucha documentación para aprender a programar videojuegos, la mayoría como sabrás, en inglés (es lo que hay) de todas maneras en esta comunidad tratamos de generar y recomendar contenido en español y voy a intentar que así sea en esta pequeña guía de como aprender a programar videojuegos con C++.

1. Aprende C++
Parece obvio, pero a veces no lo es tanto. Para empezar basta con usar un editor de texto con el que generar los ficheros fuentes y un complidador, yo recomiendo el compilador gnu gcc (g++ para C++) y el IDE Codelite. Pero eso es cuestión de cada uno, hay muchos IDE y compiladores C++.

Una ves tengas eso necesitas un manual/curso/tutorial de C++ que te enseñe desde el principio. Puedes comprar un libro (hay muchos y muy buenos) buscar alguno en ingés en google (también los hay muy buenos), pero como dijimos antes vamos a tratar de darte una opción en español. Yo te recomiendo el curso de ConClase es muy popular y de los mas visitados en la red, un curso completo de C++ en español y gratuito.

http://c.conclase.net/curso/
A la vez que aprendes C++ te recomiendo que busques y leas mucho acerca de Algoritmos, métodos de programación y estructuras de datos. Lee y lee mucho pues es la única forma de aprender.

Una web que debería tener a mano todo programador de C++ es la siguiente, contiene toda la referencia de las bibliotecas estándar de C++, es bueno tenerla a mano siempre.

http://www.cplusplus.com/reference/ (inglés)
2. La STL

Una vez te defiendas con C++ a un nivel básico te darás cuenta que hacer ciertas cosas es algo tedioso, existe una colección de Bibliotecas que son un Standard de C++ y que la mayoría de los compiladores actuales trae llamada STL, La STL es casi fundamental para no volverte loco cuando los programas se vuelven complejos y necesitas tipos de datos avanzados. Descubrirás que contiene plantillas que facilitan el uso de tipos de datos avanzados como cadenas, vectores, diccionarios y un largo etcétera de utilidades.

A continuación dejo algunos documentos muy útiles para aprender a usar la STL.

http://geneura.ugr.es/~gustavo/stl/stl.html
http://decsai.ugr.es/~jfv/ed1/c++/cdrom4/paginaWeb/stl.htm
http://www.zator.com/Cpp/E5.htm
http://gabarro.org/wiki/STL_vector
http://mictlan.utm.mx/arreglos.html

3. Vamos a por los juegos

Aunque espero que durante las etapas anteriores hayas tenido imaginación y empezaras a crear tus propios juegos en modo consola tales como ahorcados, adivina el número, hundir la flota, etc. Ha llegado la parte de empezar con aplicaciones gráficas. Aquí uno debe elegir que camino tomar. Para los videojuegos el camino sería aprender a usar una biblioteca 2D. Muchos se desilusionan en este punto nueva mente porque ellos quieren hacer super juegos 3D que compitan con el último juego de moda, pero primero se debe aprender a caminar para después correr. Aprender como se trabaja en un proyecto de videojuego y con bibliotecas externas, aprender a usar imágenes y conceptos de programación gráfica.

Existen varias bibliotecas gráficas con las que empezar, pero mi recomendación es SDL, tiene bastante reputación en el mundo de los videojuegos 2D, es simple y existe mucha documentación. En este apartado es cuando es más difícil encontrar documentación en español, pero existe un gran trabajo en español en forma de wiki perfecto para comenzar en este maravilloso mundo.

http://softwarelibre.uca.es/wikijuegos/
Esto estupendo tutorial no se limita a mostrat y explicar las características de SDL, sino que trata de explicar el buen uso de la misma y finalmente tiene el desarrollo de un videojuego paso a paso. Totalmente recomendada para iniciarse con gráficos.

4. Sigue aprendiendo
A estas alturas ya habrás aprendido una base y sabrás que camino debes seguir, que cosas debes aprender y como tienes que moverte. En todo momento tienes que estar leyendo, documentándote y aprendiendo es la única forma de poder entrar en este mundo del desarrollo de videojuegos que está avanzando cada día. Para eso te recomiendo un par de webs sobre programación de videojuegos, desgraciadamente, la mayoría en inglés.

http://gpwiki.org/ – Impresindible tenerla en favoritos para todo programador de videojuegos. La wiki por excelencia del desarrollo de videojuegos.
http://www.gamedev.net/ – Otra muy importante, comunidad muy activa, te enterarás de las últimas novedades y en los foros siempre hay alguien dispuesto ayudar, siempre que sepas inglés.
http://www.gamasutra.com/ – A tener muy encuenta, ofertas de trabajo, grandes artículos… Añadela a tu lista de webs a  mirar cada día.
http://www.stratos-ad.com/ – La comunidad más grande de desarrollo en español. Puedes encontrar muy buenas cosas en los foros.
http://www.google.com/ – No no es coña, está es la más importante de todas. Lo de arriba es una guía para comenzar, pero te surgirán miles de dudas a cada paso que des. Google es tu amigo, lo sabe casi todo y lo resuelve casi todo si sabes buscar bien. Aprende si no sabes a usar todos los trucos de google. Un programador y más uno de videojuegos debe ser autodidacta y aprender a buscarse la vida en este competitivo y apasionante mundo.
Espero que te sirva de algo esta pequeña guía si quieres adentrarte en el mundo de la programación de videojuegos y no sabias como hacerlo.

Libreria Allegro 5
En este tutorial vamos a ver como usar la librería Allegro 5. Allegro es una librería de C, que se usa para implementar juegos, mayormente 2D, dado que la librería nos da todas las funcionalidades para dibujo, gestión de sprites, buffering, captura de eventos de usuario (teclado y ratón), música, e includo multithreading.

Además, es multiplataforma, lo que significa que podemos compilar nuestro código fuente en sistemas Windows y Linux, usando los mismos ficheros de cabecera. Lo que cambia es las librerías de linkado.


Puedes descargarte el código para reste tutorial de github: https://github.com/aalmunia/allegro5-base
Primero de todo, tenemos que instalar Allegro 5. En Windows, con Visual Studio, es trivialmente sencillo. Solo sigue las instrucciones del siguiente artículo:
Instalar Allegro 5 en Visual Studio con NuGet, y NuGet, el gestor de paquetes de Visual Studio, hará el trabajo duro por ti.
En Linux, requiere algo mas de trabajo, así que vamos a ver como realizarlo, desde el principio. Lo mejor es descargar el código fuente de Allegro 5 de aquí: https://github.com/liballeg/allegro5, usando el comando
git clone https://github.com/liballeg/allegro5
para ello. Te creará una carpeta /allegro5 a partir de donde hayas ejecutado el comando de clonado.
Primero, es una buena idea actualizar la caché de apt-get, usando el siguiente comando para ello:
sudo apt-get update
Y ahora, para poder compilar la librería, con todos los módulos adicionales que trae, debemos ejecutar el siguiente comando:
sudo apt-get install -y libgl1-mesa-dev libglu1-mesa-dev cmake build-essential make libxcursor-dev cmake g++ freeglut3-dev libxcursor-dev libpng12-dev libjpeg-dev libfreetype6-dev libgtk2.0-dev libasound2-dev libpulse-dev libopenal-dev libflac-dev libdumb1-dev libvorbis-dev libphysfs-de
Si todo ha ido bien y ha instalado las librerías, ya estamos listos para compilar Allegro 5. Para crear la versión compilada de la librería, primero crearemos una carpeta llamada /build, en la que irá la librería compilada. Vayamos a la carpeta en la que está el código fuente que hemos clonado y ejecutemos los siguientes comandos:
cd allegro5
mkdir build
cd build
cmake ..
Si la ejecución de cmake ha ido correctamente, ya podemos compilar la librería y luego instalarla:
make
sudo make install
Y con esto, deberían quedar las librerías instaladas, generalmente en la carpeta /usr/local/lib, habiendo creado una serie de ficheros con extensión .so, (Shared Object), que es el equivalente a un fichero DLL en Windows. Para comprobar que la librería funciona correctamente, vamos a testearla con un sencillo programa:








allegro5_base_test.cpp
  1. #include <string>
  2. #include <allegro5/allegro.h>
  3. int main(int argc, char** argv) {
  4. al_init();
  5. ALLEGRO_DISPLAY* pDisplay = al_create_display(400, 400);
  6. std::string sWindowTitle = "Nuestra primera aplicación Allegro 5";
  7. al_set_window_title(pDisplay, sWindowTitle.c_str());
  8. while(true) {
  9. al_flip_display();
  10. }
  11. return 0;
  12. }

Analizando el código, vemos que lo primero es la inclusión de dos librerías, string y el core de allegro5. Dado que en C las cadenas son de tipo char*, y es una pesadez trabajar con ellas de esta forma, usaremos siempre que nos sea posible la clase string de la librería estándar. La llamada a al_init() es fundamental. Sin ella, ninguna función de allegro va a funcionar. Tras ello, declaramos un puntero a un tipo ALLEGRO_DISPLAY, que representa la ventana en la que se renderizará nuestra aplicación. Le decimos que la queremos de 400×400, y le damos un título llamando a al_set_window_title. Por último, el bucle infinito típico de un juego. Mientras que el usuario no pulse teclas ni interaccione con la aplicación, esta renderizará el estado actual indefinidamente, hasta que algo suceda.
Para poder cerrar esta aplicación, que de momento solo muestra una ventana de 400×400, con el título, tenemos que pulsar CTRL+C en la consola. Esto es así, porque todavía no hemos implementado un sistema de captura de eventos, y la ventana no responde al evento de pulsar en el icono de cerrar de la misma hasta que no lo programemos. Hagamos lo siguiente: Si el usuario pulsa en el icono de cerrar, o si pulsa la tecla ESCAPE, salimos de la aplicación. Vamos a modificar nuestro código para contemplar este caso:








allegro5_base_test.cpp
  1. #include <string>
  2. #include <allegro5/allegro.h>
  3. int main(int argc, char** argv) {
  4. al_init();
  5. al_install_keyboard();
  6. al_install_mouse();
  7. ALLEGRO_DISPLAY* pDisplay = al_create_display(400, 400);
  8. ALLEGRO_EVENT_QUEUE* oQueue;
  9. oQueue = al_create_event_queue();
  10. al_register_event_source(oQueue, al_get_keyboard_event_source());
  11. al_register_event_source(oQueue, al_get_display_event_source(pDisplay));
  12. std::string sWindowTitle = "Nuestra primera aplicación Allegro 5";
  13. al_set_window_title(pDisplay, sWindowTitle.c_str());
  14. while(true) {
  15. ALLEGRO_EVENT oEvent;
  16. al_wait_for_event(oQueue, &oEvent);
  17. switch(oEvent.type) {
  18. case ALLEGRO_EVENT_DISPLAY_CLOSE:
  19. al_destroy_display(pDisplay);
  20. return 0;
  21. break;
  22. case ALLEGRO_EVENT_KEY_DOWN:
  23. al_destroy_display(pDisplay);
  24. return 0;
  25. break;
  26. default:
  27. break;
  28. }
  29. al_flip_display();
  30. }
  31. return 0;
  32. }

Como vemos, el código se modifica considerablmente. Tenemos que llamar a al_init_* para tener disponibles eventos de teclado y ratón. Las instrucciones:








allegro5_base_test.cpp
  • ALLEGRO_EVENT_QUEUE* oQueue;
  • oQueue = al_create_event_queue();
  • al_register_event_source(oQueue, al_get_keyboard_event_source());
  • al_register_event_source(oQueue, al_get_display_event_source(pDisplay));

crean la cola de eventos, y le dicen al programa que esta cola de eventos recibe eventos de dos fuentes: el teclado y el ratón. La instrucción
al_wait_for_event(oQueue, &oEvent);
es interesante. Estamos escuchando eventos, y al producirse uno, pasamos por referencia el objeto de tipo ALLEGRO_EVENT previamente creado, de tal forma que se rellena al entrar en esa función. Posteriormente, revisamos el tipo del evento, y si es alguno de los dos casos anteriormente descritos, cerramos la aplicación. Allegro provée de una inmensa cantidad de eventos tipados, desde pulsaciones de teclado y ratón, hasta Joystick y temporizadores, con todas las características del evento en cada caso. Realmente, es la gestión de toda esta enorme cantidad de eventos, lo que suele ser el grueso de una aplicación tipo juego escrita en Allegro. En este sentido, es muy sencillo programar qué pasa cuando pulsamos ciertas teclas, y dejar el juego corriendo mientras tanto. También podemos definir nuestros propios eventos, en un futuro tutorial veremos mas sobre este tema.
El problema del código anterior es su poca reutilización. Para solucionar este problema, vamos a encapsular la funcionalidad de inicialización de la aplicación dentro de una clase que llamaremos BaseApplication. Esta clase tendrá una serie de métodos, siendo, al menos uno de ellos, virtual, para que la clase que herede de ella lo implemente, y de esa forma, la aplicación se inicie. Vamos a ver el código fuente de nuestra clase BaseApplication. En C++, lo habitual es separar el código de la especificación de la clase de la implementación de la misma. La definición se suele escribir en un fichero de cabecera, con extensión .h, mientras que la implementación se guarda en un fichero de extensión .cpp








BaseApplication.h
  1. #include <allegro5/allegro.h>
  2. #include <string>
  3. #include <vector>
  4. #include <stdio.h>
  5. #include <iostream>
  6. #ifndef APPLICATION_H
  7. #define APPLICATION_H
  8. class BaseApplication {
  9. public:
  10. BaseApplication();
  11. BaseApplication(const BaseApplication& orig);
  12. BaseApplication(unsigned int iWidth, unsigned int iHeight, std::string sName);
  13. void initApp();
  14. virtual int mainLoop() = 0;
  15. virtual ~BaseApplication();
  16. protected:
  17. ALLEGRO_DISPLAY *m_pDisplay = NULL;
  18. unsigned int m_iWindowWidth = 0;
  19. unsigned int m_iWindowHeight = 0;
  20. std::string m_sWindowName;
  21. ALLEGRO_COLOR m_oColRed;
  22. ALLEGRO_COLOR m_oColGreen;
  23. ALLEGRO_COLOR m_oColBlue;
  24. ALLEGRO_COLOR m_oColWhite;
  25. ALLEGRO_EVENT_QUEUE* m_pEventQueue;
  26. void initColors();
  27. };
  28. #endif /* APPLICATION_H */

Y ahora, veamos el fichero de implementación de la clase:








BaseApplication.cpp
  1. #include <allegro5/allegro.h>
  2. #include <allegro5/allegro_image.h>
  3. #include "BaseApplication.h"
  4. BaseApplication::BaseApplication() {
  5. }
  6. BaseApplication::BaseApplication(const BaseApplication& orig) {
  7. }
  8. BaseApplication::BaseApplication(unsigned int iWidth, unsigned int iHeight, std::string sName) {
  9. this->m_iWindowWidth = iWidth;
  10. this->m_iWindowHeight = iHeight;
  11. this->m_sWindowName = sName;
  12. }
  13. void BaseApplication::initApp() {
  14. this->m_pDisplay = al_create_display(this->m_iWindowWidth, this->m_iWindowHeight);
  15. if (this->m_pDisplay != NULL) {
  16. al_set_window_title(this->m_pDisplay, this->m_sWindowName.c_str());
  17. this->m_bInit = true;
  18. this->initColors();
  19. al_clear_to_color(this->m_oColWhite);
  20. std::cout << "Initialized OK" << std::endl;
  21. }
  22. }
  23. void BaseApplication::initColors() {
  24. this->m_oColRed = al_map_rgb(255, 0, 0);
  25. this->m_oColGreen = al_map_rgb(0, 255, 0);
  26. this->m_oColBlue = al_map_rgb(0, 0, 255);
  27. this->m_oColWhite = al_map_rgb(255, 255, 255);
  28. }
  29. BaseApplication::~BaseApplication() {
  30. if (this->m_pDisplay != NULL) {
  31. al_destroy_display(this->m_pDisplay);
  32. }
  33. if (this->m_pEventQueue != NULL) {
  34. al_destroy_event_queue(this->m_pEventQueue);
  35. }
  36. }

Parece bastante complejo, pero en realidad es muy simple. Dado que siempre, cuando escribamos una aplicación de Allegro vamos a querer ciertas funcionalidades disponibles, lo mejor es programarlas en una clase que tenga algún método virtual, de tal forma que siempre tendremos disponible la pantalla, el teclado, el ratón, etc… Solo tenemos que preocuparnos del código de nuestra aplicación, en vez de todo el código que conlleva inicializar la aplicación. Además, de esa forma, si en un futuro agregamos una funcionalidad a nuestra clase base (la capacidad de recibir ciertos parámetros de entrada para su configración, por ejemplo), lo tendremos disonible en el resto de aplicaciones que hayamos escrito con esta clase como base. Para nuestro ejemplo, escribamos una clase que herede de BaseApplication:








Application.h
  1. #pragma once
  2. #ifndef APPLICATION_H
  3. #define APPLICATION_H
  4. #include <string>
  5. #include "BaseApplication.h"
  6. class Application :
  7. public BaseApplication
  8. {
  9. public:
  10. Application(unsigned int iWidth, unsigned int iHeight, std::string sName) : BaseApplication(iWidth, iHeight, sName) {
  11. al_init();
  12. al_install_mouse();
  13. al_install_keyboard();
  14. };
  15. virtual ~Application();
  16. int mainLoop();
  17. };
  18. #endif
  19. </string>

Y el código de la implementación:








Application.h
  1. #include "Application.h"
  2. Application::~Application()
  3. {
  4. }
  5. int Application::mainLoop() {
  6. this->m_pEventQueue = al_create_event_queue();
  7. al_register_event_source(this->m_pEventQueue, al_get_keyboard_event_source());
  8. al_register_event_source(this->m_pEventQueue, al_get_display_event_source(this->m_pDisplay));
  9. while (true) {
  10. ALLEGRO_EVENT oEvent;
  11. al_wait_for_event(this->m_pEventQueue, &oEvent);
  12. switch (oEvent.type) {
  13. case ALLEGRO_EVENT_DISPLAY_CLOSE:
  14. return 0;
  15. break;
  16. case ALLEGRO_EVENT_KEY_DOWN:
  17. return 0;
  18. break;
  19. }
  20. al_flip_display();
  21. }
  22. }

Como vemos, el código es bastante menos extenso que el de la clase base, porque toda la inicialización ya está hecha. El constructor usa una nueva funcionalidad de C++11, que permite iniicalizar el contructor de la clase base, al tiempo que llamamos a nuestra propia inicialización para la clase hija. El código interesante está en el método mainLoop, en el que lo que hemos hecho es encapsular la funcionalidad de cerrar la aplicación si pulsamos ESCAPE, o pulsamos en el icono de cerrar de la ventana.
Ahora, el proceso de compilación. Si estás usando Visual Studio, ese proceso es un par de clicks, pero si estás usando Linux, es algo mas complicado. Podemos usar NetBeans o Code::Blocks, y lo único que tendríamos que hacer es configurar el proyecto para que, a la hora de realizar el linkado, agregue las librerías que están, si hemos seguido el proceso de instalación descrito, en /usr/local/lib, y se llaman liballegro*.so. Pero si queremos realizar la compilación en consola, deberíamos escribir un script llamado Makefile. Es muy sencillo, vamos a ver el código a continuación:








Makefile
  1. CC=g++
  2. CFLAGS=-std=c++11
  3. all: application
  4. application: main.o BaseApplication.o Application.o
  5. $(CC) main.o BaseApplication.o Application.o -AllegroApplication1
  6. main.o: main.cpp
  7. $(CC) $(CFLAGS) main.cpp
  8. BaseApplication.o: BaseApplication.cpp
  9. $(CC) $(CFLAGS) BaseApplication.cpp
  10. main.o: main.cpp
  11. $(CC) $(CFLAGS) main.cpp
  12. clean:
  13. rm *o AllegroApplication1

Aunque, sinceramente, recomiendo usar un IDE, como Visual Studio, NetBeans, Eclipse, Code::Blocks, o el que te de la gana. No obstante, es interesante conocer como compilar y linkar programas en línea de comandos, por si nos hiciese falta.
C++ ofrece muchas capacidades que, desde mi punto de vista, compensan la complejidad añadida de trabajar con el. Aunque Allegro está escrita en C,y la mayoría de ejemplos que puedes encontrar (incluso los que vienen con la propia librería), están escritos en C, mantener una base de código reutilizable es mas sencillo usando orientación a objetos que librerías de funciones. Es por eso que en el siguiente tutorial usaremos la clase Application que hemos implementado, para programar un sencillísimo juego.



Bueno, quisiera mostrarles el primer juego que he realizado a base de puro código en C++
El juego lo hice por medio de la biblioteca Allegro (librería gratuita y libre para desarrollar juegos en C++).


Es un prototipo de Pac-Man, y cuenta con:
Puntos
Vidas
IA básica del enemigo
1 nivel de prueba
Estrellas (aunque lo único que hacen es darte más puntos al tocarlas).

Para hacer el mapa, usé una matriz de 15x15 donde cada número es un objeto del mapa.
Los controles son las teclas de dirección.

Descarga: Archivo .zip con el juego y el código [Google Drive]


El .zip ya incluye el código y un archivo de proyecto para Code Blocks (.cbp). Para compilarlo, se necesita la librería Allegro 4 y el compilador MinGW. Cuando abran el proyecto, deben seleccionar el target "Windows Debug" o "Windows Release" para producir un ejecutable de Windows.

                                               











                                               












No hay comentarios:

Publicar un comentario