Anuncios

Bienvenidos sean a este post, hoy veremos como cargar los otros estados desde un archivo.

Anuncios

En el post anterior, vimos como cargar el estado MENU desde estados.xml. Y como mencionamos, en este post agregaremos los restantes. Para ello, modificaremos a estados.xml en nuestro proyecto sino lo poseen les dejo un link para descargarlo:

Proyecto Juego

Anuncios

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.

Anuncios

Como dijimos debemos ir a estados.xml y lo modificaremos de la siguiente manera:

estados.xml
<ESTADOS>
<MENU>
    <texturas>
        <textura archivo="assets/jugar.png" id="botonJugar" />
        <textura archivo="assets/salir.png" id="botonSalir" />
    </texturas>

    <objetos>
        <objeto tipo="BotonMenu" x="100" y="100" ancho="400" 
        alto="100" idTextura="botonJugar" numFrames="0"
        idCallback="1" />
        <objeto tipo="BotonMenu" x="100" y="300" ancho="400" 
        alto="100" idTextura="botonSalir" numFrames="0"
        idCallback="2" />
    </objetos>
</MENU>

<JUGAR>
    <texturas>
        <textura archivo="assets/helicoptero.png" id="helicoptero" />
        <textura archivo="assets/helicoptero2.png" id="helicotero" />
    </texturas>

    <objetos>
        <objeto tipo="Jugador" x="500" y="100" ancho="128" 
        alto="55" idTextura="helicoptero" numFrames="4" />
        <objeto tipo="Enemigo" x="100" y="100" ancho="128" 
        alto="55" idTextura="helicotero" numFrames="4" />
    </objetos>
</JUGAR>

<PAUSA>
    <texturas>
        <textura archivo="assets/retomar.png" id="botonretomar" />
        <textura archivo="assets/volver.png" id="botonvolver" />
    </texturas>

    <objetos>
        <objeto tipo="BotonMenu" x="100" y="100" ancho="400" 
        alto="100" idTextura="botonretomar" numFrames="0"
        idCallback="1" />
        <objeto tipo="BotonMenu" x="100" y="300" ancho="400" 
        alto="100" idTextura="botonvolver" numFrames="0"
        idCallback="2" />
    </objetos>
</PAUSA>

<GAMEOVER>
    <texturas>
        <textura archivo="assets/over.png" id="gameover" />
        <textura archivo="assets/volver.png" id="volvermenu" />
        <textura archivo="assets/reiniciar.png" id="reiniciar" />
    </texturas>

    <objetos>
        <objeto tipo="GraficosAnimados" x="200" y="100" ancho="190" 
        alto="30" idTextura="gameover" numFrames="2"
        velocAnim="1" />
        <objeto tipo="BotonMenu" x="100" y="170" ancho="400" 
        alto="100" idTextura="volvermenu" numFrames="0"
        idCallback="1" />
        <objeto tipo="BotonMenu" x="100" y="300" ancho="400" 
        alto="100" idTextura="reiniciar" numFrames="0"
        idCallback="2" />
    </objetos>
</GAMEOVER>
</ESTADOS>
Anuncios

Para esta ocasion, agregamos los estados faltantes con sus respectivas texturas, datos de tamaño, posicion, identificacion, y tambien las funciones de callback que sean necesarias, u otros atributos (p.e la velocidad de animacion). Nuestro siguiente paso sera ir a modificar todo lo necesario para poder implementarlo. Esto es muy parecido a lo visto en el post anterior, pero con menos trabajo porque hay mucho ya implementado.

Anuncios

Cargando al estado Jugar

Comencemos con el primer cambio, para ello debemos ir a EstadoJugar.cpp y modificaremos a la funcion enIngreso de la siguiente manera:

bool EstadoJugar::enIngreso() {
	EstadoParser estadoParser;
	estadoParser.estadoParse("estados.xml", e_idjugar, &m_objetosJuego, 
		&m_listaIdtexturas);

	OutputDebugStringA("Ingresando al Estado de jugar\n");
	return true;
}
Anuncios

Basicamente, lo que hicimos fue eliminar todas las definiciones de los objetos y lo reemplazamos por la funcion encargada de tomar los datos desde estados.xml. Como es el estado JUGAR, este tomara todos los objetos en esta seccion, para este caso son Jugador y Enemigo. Para entender como funciona, les reccomiendo visitar este post donde explicamos como trabaja la clase EstadoParser. Realicemos la siguiente modificacion, y para ello modificaremos a enSalida de la siguiente manera:

bool EstadoJugar::enSalida() {
	for (int i = 0; i < m_objetosJuego.size(); i++)
		m_objetosJuego[i]->limpiar();

	for (int i = 0; i < m_listaIdtexturas.size(); i++) {
		Elmanejador::instanciar()->limpiaMapaTexturas(m_listaIdtexturas[i]);
	}

	OutputDebugStringA("Saliendo del Estado de jugar\n");
	return true;
}
Anuncios

Es muy parecida a la anterior pero en lugar de eliminar las texturas de manera especifica, utilizamos un bucle donde eliminara todas las texturas almacenadas en la lista correspondiente. Con esto, terminamos con las modificaciones en esta clase.

Anuncios

Cuando creamos a la clase de fabrica, hicimos una serie de modificaciones para la clase Jugador y Enemigo. En la clase Jugador solo haremos una modificacion; para este eliminaremos (no comentaremos) al constructor que recibe argumentos y solo dejaremos la predeterminada. Veamos los bloques que debemos eliminar:

Jugador.h
Jugador(const CargadorParams*);
Anuncios
Jugador.cpp
Jugador::Jugador(const CargadorParams* pParams) :
	SDLObjetoJuego(pParams) {
}
Anuncios

Pasemos a la clase Enemigo y primero debemos repetir lo hecho anteriormente. Es decir, eliminaremos al constructor que recibe los argumentos:

Enemigo.h
Enemigo(const CargadorParams*);
Anuncios
Enemigo.cpp
Enemigo::Enemigo(const CargadorParams* pParams) :
	SDLObjetoJuego(pParams) {
	m_velocidad.setY(2);
	m_velocidad.setX(0.001f);
}
Anuncios

Con esto realizado, debemos hacer una modificacion mas. Vamos a la funcion cargar y modificaremos su codigo de la siguiente manera:

void Enemigo::cargar(const CargadorParams* pParams) {
	SDLObjetoJuego::cargar(pParams);
	m_velocidad.setY(2);
}
Anuncios

Agregamos el movimiento vertical del objeto mediante la funcion setY. Con esto realizado, podemos pasar a trabajar con el siguiente estado.

Anuncios

Cargando el estado Pausa

Para esto vamos a modificar a la clase EstadoPausa, primero iremos al archivo de encabezado. En este modificaremos de quien sera heredero, y buscaremos la siguiente linea:

class EstadoPausa : public EstadoJuego
Anuncios

Y la modificaremos de la siguiente manera:

class EstadoPausa : public EstadoMenu
Anuncios

Al igual que sucedio con EstadoMenuPpal, todos estos estados seran herederos de EstadoMenu. En este mismo archivo, debemos agregar el siguiente prototipo en la parte privada:

virtual void setCallbacks(const std::vector<Callback>&);
Anuncios

Esta funcion es abstracta en EstadoMenu y al ser heredada, debemos definirla. Con nuestro prototipo declarado, debemos pasar a definirlo en el archivo .cpp:

void EstadoPausa::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()]);
		}
	}
}
Anuncios

Esta funcion se encarga de asignar la correspondiente funcion al elemento. En este caso, pasara por todos los elementos y si son de tipo BotonMenu procede a asignarlos, ignorando al resto. El siguiente paso sera modificar a enIngreso de la siguiente manera:

bool EstadoPausa::enIngreso() {
	EstadoParser estadoParser;
	estadoParser.estadoParse("estados.xml", e_idpausa, &m_objetosJuego,
		&m_listaIdtexturas);
	m_callbacks.push_back(0);
	m_callbacks.push_back(e_retomarJuego);
	m_callbacks.push_back(e_volverMenu);
	setCallbacks(m_callbacks);

	OutputDebugStringA("Entrando en estado Pausa\n");
	return true;
}
Anuncios

Es muy similar a lo visto anteriormente pero con un par de diferencias. La similitud en la apertura del archivo para obtener todos los elementos pertenecientes a este estado. Ahora, la diferencia esta en el agregado de las funciones callback para ejecutar cuando apretemos los botones. El primero debe ser cero para diferenciarlos de los otros id que pasamos en nuestro archivo. Para finalmente, enviar a setCallbacks todos las funciones agregadas. Pasemos a modificar la funcion enSalida de la siguiente manera:

bool EstadoPausa::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]);
	}
	Controles::instanciar()->limpiar();

	OutputDebugStringA("Saliendo del estado Pausa\n");
	return true;
}
Anuncios

En esta oportunidad, la funcion es muy similar pero modificamos el llamado de eliminacion de texturas especificos por un bucle que buscara todos los almacenados y procede a limpiarlos. El resto sigue trabajando como antes, simplemente liberando de memoria todo el contenido. Solo nos resta una modificacion, procedamos con ella.

Anuncios

Cargando el estado Game Over!

Antes de pasar a trabajar con este estado debemos hacer una pequeña modificacion sobre una clase que nos olvidamos, GraficosAnimados. Primero, vayamos a GraficosAnimados.h y agreguemos al inicio la siguiente linea:

#include "FabricaObjetosJuego.h"
Anuncios

Esta sera para poder acceder y utilizar a la clase CreadorBase. Nuestro siguiente paso ser agregar el siguiente codigo al final del archivo dentro del bloque #ifndef:

class CreadorGraficoAnimado : public CreadorBase
{
public:
	ObjetoJuego* crearObjetoJuego() const;
};
Anuncios

Esta es la clase creadora que hemos agregado en los distintos elementos que tenemos para poder registrarlos y ser utilizados. En esta tendremos este prototipo para crear el objeto, y en un momento lo definiremos. Lo siguiente sera agregar estos dos prototipos en la parte publica pero de la clase GraficosAnimados:

GraficosAnimados();
void cargar(const CargadorParams*);
Anuncios

El primero sera el constructor predeterminado que debemos usar para crear los nuevos objetos, y el segundo es para la funcion cargar para enviar todos los datos del elemento. Con esto, tenemos finalizado nuestro archivo de encabezado. Pasemos a definir todas estas nuevas funciones en GraficosAnimados.cpp:

ObjetoJuego* CreadorGraficoAnimado::crearObjetoJuego() const {
	return new GraficosAnimados();
}

GraficosAnimados::GraficosAnimados() : SDLObjetoJuego() {}

void GraficosAnimados::cargar(const CargadorParams* pParams) {
	SDLObjetoJuego::cargar(pParams);
	m_velocAnim = pParams->getVelocAnim();
	m_numFrames = pParams->getNumFrames();
}
Anuncios

La primera solo crea el objeto de tipo GraficosAnimados, y para ello usa el constructor predeterminado. Lo siguiente es definirlo, y solamente llama al constructor de SDLObjetoJuego para iniciarlo tambien. En la funcion cargar, seguimos pasando todos los parametros al cargar de SDLObjetoJuego pero tambien establecemos los valores de las variables mediante sus correspondientes funciones desde el argumento. Solo nos resta una ultima modificacion en esta clase, y es la eliminacion de los constructores que pasan argumentos:

GraficosAnimados.h
GraficosAnimados(const CargadorParams*, int);
Anuncios
GraficosAnimados.cpp
GraficosAnimados::GraficosAnimados(const CargadorParams* pParams,
	int velocAnim) : SDLObjetoJuego(pParams), m_velocAnim(velocAnim) {
	m_numFrames = pParams->getNumFrames();
}
Anuncios

Nuestra ultima modificacion sera en la clase EstadoFinal. Lo primero que haremos es ir a EstadoFinal.h y buscaremos la siguiente linea:

class EstadoFinal : public EstadoJuego
Anuncios

Y la modificaremos de la siguiente manera:

class EstadoFinal : public EstadoMenu
Anuncios

Al igual que en los casos anteriores, debemos convertirla en heredera de EstadoMenu. Por esta razon, debemos agregar el siguiente prototipo en la parte privada:

virtual void setCallbacks(const std::vector<Callback>&);
Anuncios

Es exactamente lo mismo que vimos en los estados anteriores. Nuestro siguiente paso es ir a EstadoFinal.cpp y agregaremos la definicion de la nueva funcion:

void EstadoFinal::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()]);
		}
	}
}
Anuncios

Es exactamente la misma accion que comentamos en el estado anterior. El siguiente paso es modificar a enIngreso de la siguiente manera:

bool EstadoFinal::enIngreso() {
	EstadoParser estadoParser;
	estadoParser.estadoParse("estados.xml", e_idfinal, &m_objetosJuego,
		&m_listaIdtexturas);
	m_callbacks.push_back(0);
	m_callbacks.push_back(e_volverMenu);
	m_callbacks.push_back(e_reiniciar);
	setCallbacks(m_callbacks);

	OutputDebugStringA("Entrando al estado Game Over\n");
	return true;
}
Anuncios

Nuevamente, todos los datos desde el archivo y establecemos las funciones de callback. Nuestra ultima modificacion sera en la funcion enSalida:

bool EstadoFinal::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 de Game Over\n");
	return true;
}
Anuncios

Y como los casos anteriores, eliminamos el llamado manual de eliminacion de texturas para utilizar un bucle y eliminar los almacenados. Con todo esto realizado, solo nos resta una ultima implementacion.

Anuncios

Para esto, debemos ir a la funcion iniiciar en Juego.cpp y eliminaremos el siguiente bloque:

m_objetosJuego.push_back(new Jugador(
    new CargadorParams(50, 100, 128, 82, "animado")));
m_objetosJuego.push_back(new Enemigo(
    new CargadorParams(300, 300, 128, 82, "animado")));
Controles::instanciar()->iniciarJoysticks();
Anuncios

Lo que hicimos fue eliminar la creacion de los objetos para el jugador y el enemigo. Lo siguiente sera registrar los nuevos tipos, y para ello agregaremos las siguientes lineas junto al registro anterior y de la creacion del estado de maquina:

Elfabricante::instanciar()->registrarTipo("Jugador", new CreadorJugador());
Elfabricante::instanciar()->registrarTipo("Enemigo", new CreadorEnemigo());
Elfabricante::instanciar()->registrarTipo("GraficosAnimados", new CreadorGraficoAnimado());
Anuncios

Son los nuevos tipos de datos que agregamos en el archivo y seran solicitados por todas las funciones anteriores. Con todo esto realizado, podemos compilarlo y ver como funciona. Veamos el siguiente video:

Anuncios

Como pueden ver en el video, tenemos todo completamente funcional pero en lugar de ser definido en nuestro codigo; extrae todos los datos desde un archivo. Y recuerden que al final tienen un repositorio para ver todos los codigos de este post.

Anuncios

En resumen, hoy hemos agregado los estados faltantes, hemos modificado al archivo con toda la informacion necesaria, asi como tambien hemos visto las modificaciones para poder utilizarlas, asi como tambien para que sean las mas genericas posibles. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:

Cargando los otros estados / GitHub

Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:

Anuncios

Donación

Es para mantenimento del sitio, gracias!

$1.50