La aproximación orientada a objetos (3). MiniMiner 3: Personaje y enemigo como clases (*)
Ya habíamos creado las clases auxiliares "Hardware" y "ElementoGraf". Ahora vamos a empezar a descomponer la lógica de juego como una serie de objetos que cooperan entre sí. En este acercamiento vamos a incluir:
- Una clase "Presentacion", para representar a la pantalla de presentación del juego, y que tendrá solamente un método "mostrar()", que por ahora mostrará una imagen estática (aprovecharemos para que realmente sea un pantalla de presentación, y no la pantalla de juego).
- Una clase "Personaje", que heredará de "ElementoGraf" y añadirá sólo dos métodos: "moverDerecha()" y "moverIzquierda()", que desplazarán al personaje en un sentido u otro, para responder a las pulsaciones de teclas por parte del usuario del juego.
- Una clase "Enemigo", que también heredará de "ElementoGraf" y lo ampliará con un único método: "mover()", que calcule la siguiente posición del enemigo. En este juego se limitará a moverse a un lado y a otro, pero en juegos más avanzados, podría existir una cierta inteligencia en esta función "mover()", bien porque nos persiguiera siempre, bien porque lo hiciera sólo cuando estuviéramos cerca (y si estamos lejos "no nos ve"), bien, porque siguiese un patrón de movimiento más complejo.
El diagrama de clases (sin detallar los métodos ni los atributos de cada clase de objetos) podría ser:
Y ninguna de estas tres clases es complicada de crear...
El fichero de cabecera de Presentacion podría ser así:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Presentacion.h: */ /* La "presentacion" de */ /* Miniminer (version 0.03) */ /* Fichero de cabecera */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #ifndef Present_h #define Present_h #include "ElementoGraf.h" #include "Hardware.h" class Presentacion { public: Presentacion(); int mostrar(Hardware h); private: ElementoGraf cartel; }; #endif
Y el desarrollo podría ser así:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Presentacion.cpp: */ /* La "presentacion" de */ /* Miniminer (version 0.03) */ /* Fichero de desarrollo */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #include "Hardware.h" #include "ElementoGraf.h" #include "Presentacion.h" #ifdef __cplusplus #include <cstdlib> #else #include <stdlib.h> #endif using namespace std; /** constructor * y lee los records */ Presentacion::Presentacion() { cartel.crearDesdeFichero( "miner.bmp" ); } /** bienvenida: muestra la pantalla de bienvenida * y espera una tecla */ int Presentacion::mostrar(Hardware h) { int tecla; // Primero muestro el cartel antiguo hard = h; hard.borrarOculta(); cartel.moverA(0,0); hard.dibujarOculta(cartel); hard.visualizarOculta(); hard.esperarTecla(); }
Del forma similar, el fichero de cabecera de Personaje podría ser:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Personaje.h: */ /* El "personaje" de */ /* Miniminer (version 0.03) */ /* Fichero de cabecera */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #ifndef Personaje_h #define Personaje_h #include "ElementoGraf.h" #include "Hardware.h" class Personaje: public ElementoGraf { public: Personaje(); void moverDerecha(); void moverIzquierda(); }; #endif
Y su desarrollo:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Personaje.cpp: */ /* El "personaje" de */ /* Miniminer (version 0.03) */ /* Fichero de desarrollo */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #include "Hardware.h" #include "ElementoGraf.h" #include "Personaje.h" #ifdef __cplusplus #include <cstdlib> #include <time.h> #else #include <stdlib.h> #include <time.h> #endif using namespace std; /** constructor * y lee los records */ Personaje::Personaje() { posX = 200; posY = 200; crearDesdeFichero( "personaje.bmp" ); } /** moverDerecha: mueve el personaje hacia la derecha */ void Personaje::moverDerecha() { posX += 4; } /** moverIzquierda: mueve el personaje hacia la izquierda */ void Personaje::moverIzquierda() { posX -= 4; }
Finamente, el fichero de cabecera de Enemigo sería:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Enemigo.h: */ /* El "enemigo" de */ /* Miniminer (version 0.03) */ /* Fichero de cabecera */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #ifndef Enemigo_h #define Enemigo_h #include "ElementoGraf.h" #include "Hardware.h" class Enemigo: public ElementoGraf { public: Enemigo(); void mover(); private: int incrX; }; #endif
Y su desarrollo:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* Enemigo.cpp: */ /* El "enemigo" de */ /* Miniminer (version 0.03) */ /* Fichero de desarrollo */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #include "Hardware.h" #include "ElementoGraf.h" #include "Enemigo.h" #ifdef __cplusplus #include <cstdlib> #include <time.h> #else #include <stdlib.h> #include <time.h> #endif using namespace std; /** constructor * y lee los records */ Enemigo::Enemigo() { posX = 500; posY = 200; incrX = 2; crearDesdeFichero( "enemigo.bmp" ); } /** mover: mueve el personaje segn indique su lgica */ void Enemigo::mover() { posX += incrX; // Da la vuelta si llega a un extremo if ((posX > 570) || (posX < 30)) incrX = -incrX; }
Y el cuerpo del programa, que vuelve a reducir su tamaño (esta vez en 20 líneas) y a ganar en legibilidad quedaría así:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* miner02.cpp */ /* */ /* Ejemplo: */ /* Primer acercamiento a */ /* "MiniMiner" */ /* */ /* Comprobado con: */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /*------------------------------*/ #include "Hardware.h" #include "ElementoGraf.h" #include "Personaje.h" #include "Enemigo.h" #include "Presentacion.h" /* -------------- Constantes globales ------------- */ #define ANCHOPANTALLA 640 #define ALTOPANTALLA 480 /* -------------- Variables globales -------------- */ Hardware hard; Personaje *personaje; Enemigo *enemigo; Presentacion *presentacion; ElementoGraf *fragmentoSuelo; int partidaTerminada; int incrX = 4; int incrY = 4; int tecla; int ySuelo = 232; // Prototipos de las funciones que usaremos void comprobarTeclas(); void moverElementos(); void comprobarColisiones(); void dibujarElementos(); void pausaFotograma(); void moverDerecha(); void moverIzquierda(); void lanzarPresentacion(); void moverEnemigo(); void dibujarFondo(); // --- Bucle principal del juego ----- void buclePrincipal() { partidaTerminada = false; do { comprobarTeclas(); moverElementos(); comprobarColisiones(); dibujarElementos(); pausaFotograma(); } while (partidaTerminada != true); } // -- Comprobac de teclas para mover personaje o salir void comprobarTeclas() { if (hard.comprobarTecla(TECLA_ESC)) partidaTerminada = true; if (hard.comprobarTecla(TECLA_DCHA)) personaje->moverDerecha(); else if (hard.comprobarTecla(TECLA_IZQD)) personaje->moverIzquierda(); } // -- Mover otros elementos del juego void moverElementos() { enemigo->mover(); } // -- Comprobar colisiones de nuestro elemento con otros, o disparos con enemigos, etc void comprobarColisiones() { // Por ahora, no hay colisiones que comprobar } // -- Dibujar elementos en pantalla void dibujarElementos() { hard.borrarOculta(); dibujarFondo(); hard.dibujarOculta( *enemigo ); hard.dibujarOculta( *personaje ); hard.visualizarOculta(); } // -- Pausa hasta el siguiente fotograma void pausaFotograma() { // Para 25 fps: 1000/25 = 40 milisegundos de pausa hard.pausa(40); } // -- Funciones que no son de la logica de juego, sino de // funcionamiento interno de otros componentes // -- Pantalla de presentacion void lanzarPresentacion() { presentacion->mostrar(hard); } // -- Dibuja el fondo (por ahora, apenas un fragmento de suelo) void dibujarFondo() { int i; int anchoImagen = 16; for (i=0; i<15; i++) { fragmentoSuelo->moverA(i*anchoImagen, ySuelo); hard.dibujarOculta( *fragmentoSuelo ); } } /* -------------- Rutina de inicializacin -------- */ int inicializa() { hard.inicializar(640,480); personaje = new Personaje(); enemigo = new Enemigo(); presentacion = new Presentacion(); fragmentoSuelo = new ElementoGraf(); fragmentoSuelo->crearDesdeFichero("suelo.bmp"); // Y termino indicando que no ha habido errores return 0; } /* ------------------------------------------------ */ /* */ /* -------------- Cuerpo del programa ------------- */ int main() { int i,j; // Intento inicializar if (inicializa() != 0) exit(1); lanzarPresentacion(); buclePrincipal(); hard.pausa(1000); return 0; } /* Termino con la "macro" que me pide Allegro */ END_OF_MAIN();