Bienvenidos sean a este post, hasta el post anterior creamos todo lo necesario para cargar los datos pero hoy implementaremos el primero de ellos.
Antes de comenzar con nuestras modificaciones vamos a necesitar nuestro proyecto que estuvimos modificando. Sino lo poseen les dejo un link para descargarlo:
Descarguen el archivo y extraigan el contenido en el PC. Lo unico que deben hacer es revincular a SDL y SDL_Image. Para ello, les recomiendo este post donde comento como hacerlo para SDL y este otro post donde hago lo mismo para SDL_Image. Una vez que este todo funcionando correctamente, podemos continuar.
Vamos a trabajar con la clase EstadoMenu pero antes debemos hacer unas pequeñas modificaciones. Para ello, primero debemos ir a BotonMenu.h y agregaremos los siguientes prototipos en la parte publica:
void setCallback(void(*)());
int getCallbackId();
En este caso agregamos dos prototipos para distintas funciones, la primera sera para establecer la funcion para el puntero de funciones y la segunda para obtener el id de esta pero en un momento las definiremos. Recuerden que proximamente debemos recuperar el id desde otro lugar y de varios tipos de elementos. Antes de pasar a esto debemos agregar a la siguiente variable en la parte privada:
int m_idcallback;
Esta sera simplemente para almacenar nuestro identificador del callback y con esto concluimos con el archivo de encabezado. Pasemos a nuestro archivo .cpp para definir las funciones anteriores:
void BotonMenu::setCallback(void(*callback)()) { m_callback = callback; }
int BotonMenu::getCallbackId() { return m_idcallback; }
Tenemos las dos definiciones de los nuevos prototipos. setCallback recibira a la funcion y esta la asignaremos a m_callback. Y la otra, simplemente nos devuelve el valor en m_idcallback. En este post, agregamos a la funcion cargar para enviar y cargar los parametros de cada objeto. Hoy, la modificaremos de la siguiente manera:
void BotonMenu::cargar(const CargadorParams* pParams) {
SDLObjetoJuego::cargar(pParams);
m_idcallback = pParams->getIdcallback();
m_frameActual = MOUSE_FUERA;
}
Seguimos pasando los parametros recibidos a los atributos en la funcion cargar de SDLObjetoJuego. Pero despues, establecemos el valor de identificacion de callback mediante getIdcallback, ya hablaremos sobre esto, y por ultimo establecemos el valor de m_frameActual tal como haciamos con el constructor.
Pasemos a solucionar el error de cargar. Para ello, debemos ir a CargadorParams.h y en su parte publica agregaremos el siguiente prototipo:
int getIdcallback() const;
Y ahora vayamos a CargadorParams.cpp y definamos este prototipo de la siguiente manera:
int CargadorParams::getIdcallback() const { return m_idCallback; }
Como pueden ver simplemente retorna el valor que posee m_idCallback y cuando lo vimos por ultima vez establecimos que si no existe ningun valor toma de manera predeterminada el valor de 0. Con esto concluimos todo respecto a lo que representara los botones del menu de nuestro juego pero ahora pasaremos al estado que los controlara.
Hasta aqui tenemos un estado para menu general pero a partir de ahora modificaremos este concepto para crear un menu que nos servira de base para el resto de los menues. Por esta razon, vamos a modificar la clase EstadoMenu y para ello modificaremos el codigo actual del archivo de encabezado de la siguiente manera:
EstadoMenu.h
#pragma once
#ifndef __EstadoMenu__
#define __EstadoMenu__
#include "EstadoJuego.h"
#include <vector>
#include "ObjetoJuego.h"
class EstadoMenu : public EstadoJuego
{
protected:
typedef void(*Callback)();
virtual void setCallbacks(const std::vector<Callback>&) = 0;
std::vector<Callback> m_callbacks;
};
#endif // !__EstadoMenu__
EstadoMenu seguira siendo heredera de EstadoJuego pero esta vez eliminamos toda la parte publica. Establecemos la protected y a traves de esta nueva definicion la convertiremos en abstracta para que sirva como base para nuestros futuros estados. En este codigo, establecemos un nuevo puntero de funciones, una funcion para establecer los callbacks y por ultimo una coleccion para almacenar los callbacks. Con esta nueva modificacion, podemos crear los nuevos estados y antes de finalizar recuerden eliminar o comentar todo el codigo en EstadoMenu.cpp porque ahora al ser abstracta no debe recibir ningun tipo de declaracion.
Nuestro siguiente paso sera crear una nueva clase a la cual llamaremos EstadoMenuPpal, y una vez creada iremos a su archivo de encabezado y agregaremos el siguiente codigo:
EstadoMenuPpal.h
#pragma once
#ifndef __EstadoMenuPpal__
#define __EstadoMenuPpal__
#include "EstadoMenu.h"
#include <string>
#include <vector>
#include "ObjetoJuego.h"
#include "Juego.h"
#include "BotonMenu.h"
#include "EstadoParser.h"
class EstadoMenuPpal : public EstadoMenu
{
public:
virtual void actualizar();
virtual void renderizar();
virtual bool enIngreso();
virtual bool enSalida();
virtual std::string getIdEstado() const;
private:
virtual void setCallbacks(const std::vector<Callback>&);
static void e_menuParaJugar();
static void e_salirDelMenu();
static const std::string e_idmenu;
std::vector<ObjetoJuego*> m_objetosJuego;
};
#endif // !__EstadoMenuPpal__
Si lo analizamos es muy similar a lo visto en la clase EstadoMenu que teniamos antes pero con unas sutiles diferencias. La primera importante es que no es mas heredera de EstadoJuego sino de EstadoMenu. En la parte publica no hay diferencias porque seguimos con los prototipos de EstadoJuego pero en la parte privada si veremos algunas diferencias. La primera es el prototipo setCallbacks que agregamos anteriormente, luego las dos funciones que usaremos de Callbacks. La variable estatica que la identifica y por ultimo la coleccion de objetos. Con esto concluimos el archivo de encabezado pasemos al archivo .cpp:
EstadoMenuPpal.cpp
#include "EstadoMenuPpal.h"
#include <Windows.h>
const std::string EstadoMenuPpal::e_idmenu = "MENU";
std::string EstadoMenuPpal::getIdEstado() const {
return e_idmenu;
}
void EstadoMenuPpal::actualizar() {
for (int i = 0; i < m_objetosJuego.size(); i++)
m_objetosJuego[i]->actualizar();
}
void EstadoMenuPpal::renderizar() {
for (int i = 0; i < m_objetosJuego.size(); i++)
m_objetosJuego[i]->dibujar();
}
void EstadoMenuPpal::e_menuParaJugar() {
OutputDebugStringA("Boton de juego apretado\n");
Eljuego::instanciar()->getEstadoMaquina()->
cambiaEstado(new EstadoJugar());
}
void EstadoMenuPpal::e_salirDelMenu() {
OutputDebugStringA("Boton de salir apretado\n");
Eljuego::instanciar()->limpiar();
}
bool EstadoMenuPpal::enIngreso() {
EstadoParser estadoParser;
estadoParser.estadoParse("estados.xml", e_idmenu, &m_objetosJuego,
&m_listaIdtexturas);
m_callbacks.push_back(0);
m_callbacks.push_back(e_menuParaJugar);
m_callbacks.push_back(e_salirDelMenu);
setCallbacks(m_callbacks);
OutputDebugStringA("Ingresando al estado Menu\n");
return true;
}
bool EstadoMenuPpal::enSalida() {
for (int i = 0; i < m_objetosJuego.size(); i++)
m_objetosJuego[i]->limpiar();
m_objetosJuego.clear();
for (int i = 0; i < m_listaIdtexturas.size(); i++) {
Elmanejador::instanciar()->limpiaMapaTexturas(m_listaIdtexturas[i]);
}
OutputDebugStringA("Saliendo del estado menu\n");
return true;
}
void EstadoMenuPpal::setCallbacks(const std::vector<Callback>& callbacks) {
for (int i = 0; i < m_objetosJuego.size(); i++) {
if (dynamic_cast<BotonMenu*>(m_objetosJuego[i])) {
BotonMenu* pBoton = dynamic_cast<BotonMenu*>
(m_objetosJuego[i]);
pBoton->setCallback(callbacks[pBoton->getCallbackId()]);
}
}
}
Estas funciones son similares a las vistas en los casos anteriores, donde actualizar y renderizar siguen pasando por todos los elementos en m_objetosJuego tal como vimos anteriormente. La primera sera la encargada de devolvernos el valor de e_idmenu. Tenemos las funciones e_menuParaJugar y e_salirDelMenu que siguen funcionando de la misma manera, ya que se encargan de cambiar de estado y limpiar todo antes de salir, respectivamente. Veamos como es la funcion enIngreso:
bool EstadoMenuPpal::enIngreso() {
EstadoParser estadoParser;
estadoParser.estadoParse("estados.xml", e_idmenu, &m_objetosJuego,
&m_listaIdtexturas);
m_callbacks.push_back(0);
m_callbacks.push_back(e_menuParaJugar);
m_callbacks.push_back(e_salirDelMenu);
setCallbacks(m_callbacks);
OutputDebugStringA("Ingresando al estado Menu\n");
return true;
}
Primero crearemos un objeto de tipo EstadoParser, y luego usaremos la funcion estadoParse para cargar nuestro archivo. Pasamos el archivo con la informacion, y el listado de los objetos, y un dato que no existe pero no se preocupen que pronto lo solucionamos. Ingresaremos las dos funciones en m_callbacks para representar a los botones pero primero ingresamos un valor de 0. Esto lo hacemos para que la primera funcion tenga el valor 1 en lugar de 0, luego establecemos los callbacks mediante setCallbacks (nuestro siguiente paso sera definir esta funcion). Por ultimo, mostramos en la pantalla de depuracion el mensaje de ingreso y finalmente devolvemos un true. Analicemos el codigo de la funcion setCallbacks:
void EstadoMenuPpal::setCallbacks(const std::vector<Callback>& callbacks) {
for (int i = 0; i < m_objetosJuego.size(); i++) {
if (dynamic_cast<BotonMenu*>(m_objetosJuego[i])) {
BotonMenu* pBoton = dynamic_cast<BotonMenu*>
(m_objetosJuego[i]);
pBoton->setCallback(callbacks[pBoton->getCallbackId()]);
}
}
}
En esta funcion tenemos un bucle que pasara por todos los elementos contenidos en m_objetosJuego y en el cuerpo de este tenemos un condicional donde verifica mediante dynamic_cast si el tipo es BotonMenu. Si es asi genera un objeto de tipo BotonMenu con el valor actual y despues llama setCallback para agregar el id correspondiente a la funcion del boton; recuerden que esto lo hacemos mediante la propiedad que informamos en el archivo. Con esto tenemos completo a lo que sera nuestro menu principal aunque todavia nos queda una funcion mas para explicar:
bool EstadoMenuPpal::enSalida() {
for (int i = 0; i < m_objetosJuego.size(); i++)
m_objetosJuego[i]->limpiar();
m_objetosJuego.clear();
for (int i = 0; i < m_listaIdtexturas.size(); i++) {
Elmanejador::instanciar()->limpiaMapaTexturas(m_listaIdtexturas[i]);
}
OutputDebugStringA("Saliendo del estado menu\n");
return true;
}
Esta funcion es muy similar a las vistas en otras clases pero si recuerdan teniamos dos lineas que se encargaban de remover las dos texturas que estaban cargadas. A partir de esta, agregamos un bucle que pasara por todas las texturas almacenadas en m_listaIdtexturas y mediante limpiaMapaTexturas vamos eliminando a todas las texturas que hayamos agregado, En esta funcion, asi como enIngreso nos quedo un error por una propiedad que no existe. Para solucionarlo, debemos volver a la clase EstadoJuego y en el archivo de encabezado agregaremos primero esta libreria:
#include <vector>
Esta es para poder crear un vector, y luego en la clase agregaremos lo siguiente:
protected:
std::vector<std::string> m_listaIdtexturas;
Como pueden ver agregamos la parte protegida y definimos la coleccion m_listaIdtexturas que usaremos para almacenar las identificaciones de las texturas. Con esto solucionamos el inconveniente que teniamos antes pero todavia nos faltan un par de modificaciones. Recuerden que anteriormente, convertimos la clase EstadoMenu en abstracta y ya no podremos crear objetos a partir de esta. Y a su vez, para reemplazarla creamos a EstadoMenuPpal. Por esto, debemos modificar todas las creaciones de EstadoMenu a EstadoMenuPpal pero antes debemos ir a Juego.h y modificaremos la siguiente linea:
#include "EstadoMenu.h"
De la siguiente manera:
#include "EstadoMenuPpal.h"
Esto es por lo comentado anteriormente, y con esto podemos pasar a modificar las siguientes funciones:
void EstadoFinal::e_volverMenu() {
Eljuego::instanciar()->getEstadoMaquina()->
cambiaEstado(new EstadoMenuPpal());
}
void EstadoPausa::e_volverMenu() {
Eljuego::instanciar()->getEstadoMaquina()->
cambiaEstado(new EstadoMenuPpal());
}
Simplemente deben ir a los archivos .cpp de estos dos estados y modificar el llamado a EstadoMenu por EstadoMenuPpal. Nuestra siguiente modificacion sera en Juego, y para ello debemos volver al archivo encabezado y al inicio agregaremos la siguiente linea:
#include "FabricaObjetosJuego.h"
Esto nos permitira el acceso a la fabrica, y lo siguiente es ir a Juego.cpp para las ultimas modificaciones. La primera es modificar la siguiente linea en la funcion iniciar:
m_pMaquinaEstadoJuego->cambiaEstado(new EstadoMenu());
De la siguiente manera:
m_pMaquinaEstadoJuego->cambiaEstado(new EstadoMenuPpal());
Y la ultima modificacion, va a ser agregar la siguiente linea antes de la definicion de m_pMaquinaEstadoJuego en la funcion iniciar:
Elfabricante::instanciar()->registrarTipo("BotonMenu", new CreadorBotonMenu());
Si lo compilan y ejecutan, deben poder ejecutar el juego tal como lo hicimos hasta ahora pero el menu carga todos los datos desde estados.xml. El resto sigue trabajando tal como vimos en posts anteriores pero en el poximo post nos centraremos en modificar esto.
Nota:
Recuerden que mas abajo dejo un acceso a un repositorio en GitHub donde esta el codigo visto hoy.
En resumen, hoy hemos visto la primera implementacion de XML, en este caso todo lo necesario para que el menu principal cargue todos los datos, asi como todas las modificaciones para las proximas implementaciones. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:
Cargamos el estado Menu / GitHub
Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:


Donación
Es para mantenimento del sitio, gracias!
$1.50





