Anuncios

Bienvenidos sean a este post, hoy veremos como pasar los estados almacenados en un archivo XML a nuestro codigo.

Anuncios

Antes que nada, vamos a utilizar el codigo de nuestro proyecto que estuvimos trabajando en posts anteriores. 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

Lo primero que haremos es crear un nuevo archivo de tipo XML con el nombre de estados.xml. Como es uno de tipo HTML con un archivo en blanco y un simple editor como el notepad o similares, nos sera mas que suficiente. Una vez generado el archivo, le agregaremos el siguiente codigo:

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>
</JUGAR>

<GAMEOVER>
</GAMEOVER>
</ESTADOS>
Anuncios

Este es un archivo simple de tipo XML, donde tenemos un elemento raiz llamado ESTADOS, despues tenemos tres de los posibles estados del juego como son:

  • MENU
  • JUGAR
  • GAMEOVER
Anuncios
Anuncios

En nuestro archivo solamente tenemos al estado que corresponde a MENU. En este, tenemos uno llamado texturas que se encarga de enviar todos los datos correspondientes a las imagenes o texturas que mostraremos en pantalla, y su correspondiente id tal como lo hicimos en el codigo. Despues tenemos otro elemento llamado objetos, los cuales seran los botones de menu. Le pasaremos todos los datos de las imagenes y el id de la textura, tal como vinimos trabajando hasta ahora pero en lugar de cargar todo en el codigo lo pusimos en este archivo. Con esto terminamos con el codigo que tenemos hasta ahora, lo siguiente sera guardar el archivo en la carpeta donde esta nuestro codigo y al lado de donde generamos el ejecutable, en caso de querer probarlo con este.

Anuncios

Nuestro siguiente paso sera la creacion de una nueva clase que se encargara de analizar el archivo XML y manipularlo. A esta nueva clase la llamaremos EstadoParser, y una vez generada iremos a su archivo de encabezado y agregaremos el siguiente codigo:

EstadoParser.h

#pragma once
#ifndef __EstadoParser__
#define __EstadoParser__
#include <iostream>
#include <vector>
#include <string>
#include "tinyxml.h"
#include "ManejarTexturas.h"
#include "Juego.h"
#include "FabricaObjetosJuego.h"

class ObjetoJuego;

class EstadoParser
{
public:
	bool estadoParse(const char*, std::string, 
		std::vector<ObjetoJuego*>*, std::vector<std::string>*);

private:
	void objetosParse(TiXmlElement*, std::vector<ObjetoJuego*>*);
	void texturasParse(TiXmlElement*, std::vector<std::string>*);
};

#endif // !__EstadoParser__
Anuncios

Como hacemos habitualmente, esteblecemos un condicional para que no se defina mas de una vez clase en memoria. Tenemos la particularidad de incluir a tinyxml para comenzar a trabajar con los archivos .xml. En la clase definimos tres prototipos, en la parte publica uno que se encargara de leer el estado de nuestro «parser» y en la parte privada dos para la carga de los objetos y las texturas de los estados respectivamente. Con esto ya tenemos nuestro archivo de encabezado creado. Pasemos a definir estas funciones en el archivo .cpp:

EstadoParser.cpp

#include "EstadoParser.h"

bool EstadoParser::estadoParse(const char* estadoArchivo,
	std::string idEstado, std::vector<ObjetoJuego*>* pObjetos,
	std::vector<std::string>* pIdtexturas) {
	TiXmlDocument xmlDoc;
	if (!xmlDoc.LoadFile(estadoArchivo)) {
		std::cerr << xmlDoc.ErrorDesc() << std::endl;
		return false;
	}
	TiXmlElement* pRaiz = xmlDoc.RootElement();
	TiXmlElement* pEstadoRaiz = 0;
	for (TiXmlElement* e = pRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == idEstado)
			pEstadoRaiz = e;
	}
	TiXmlElement* pTexturaRaiz = 0;
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == std::string("texturas"))
			pTexturaRaiz = e;
	}
	texturasParse(pTexturaRaiz, pIdtexturas);
	TiXmlElement* pObjetoRaiz = 0;
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == std::string("objetos"))
			pObjetoRaiz = e;
	}
	objetosParse(pObjetoRaiz, pObjetos);

	return true;
}

void EstadoParser::texturasParse(TiXmlElement* pEstadoRaiz,
	std::vector<std::string>* pIdtexturas) {
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		std::string atNombreArch = e->Attribute("archivo");
		std::string idAtributo = e->Attribute("id");
		pIdtexturas->push_back(idAtributo);
		Elmanejador::instanciar()->cargar(atNombreArch, idAtributo,
			Eljuego::instanciar()->getRenderer());
	}
}

void EstadoParser::objetosParse(TiXmlElement* pEstadoRaiz,
	std::vector<ObjetoJuego*>* pObjetos) {
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		int x, y, ancho, alto, numFrames, idCallback, velocAnim;
		std::string idTextura;
		e->Attribute("x", &x);
		e->Attribute("y", &y);
		e->Attribute("ancho", &ancho);
		e->Attribute("alto", &alto);
		e->Attribute("numFrames", &numFrames);
		e->Attribute("idCallback", &idCallback);
		e->Attribute("velocAnim", &velocAnim);
		idTextura = e->Attribute("idTextura");
		ObjetoJuego* pObjetoJuego = Elfabricante::instanciar()->
			crear(e->Attribute("tipo"));
		pObjetoJuego->cargar(new CargadorParams(x, y, ancho, alto,
			idTextura, numFrames, idCallback, velocAnim));
		pObjetos->push_back(pObjetoJuego);
	}
}
Anuncios
Anuncios

Vamos a analizar este codigo, y para ello hablaremos sobre la primer funcion. Esta recibira el primer atributo que sera el nombre del archivo, el segundo sera el identificador del estado, el tercero sera un vector para los objetos que informamos en el archivo y el ultimo sera para las texturas que los representaran. Lo primero que haremos es crear un objeto de tipo TiXmlDocument que llamaremos xmlDoc, el cual sera nuestro archivo en si, y lo siguiente es un condicional donde verifica si no se pudo cargar el archivo informado en estadoArchivo. En caso de ser verdadero, procede a mostrar una descripcion del error mediante cerr y devuelve false para salir de la funcion. Si el archivo se cargo correctamente despues tendremos la siguiente linea:

TiXmlElement* pRaiz = xmlDoc.RootElement();
Anuncios

Esta linea se encarga de establecer cual es el tag raiz de los elementos de nuestro archivo, con esto ya tenemos al «contenedor» del resto de los elementos. Veamos el siguiente bloque:

	TiXmlElement* pEstadoRaiz = 0;
	for (TiXmlElement* e = pRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == idEstado)
			pEstadoRaiz = e;
	}
Anuncios

Primero crearemos un objeto de tipo TiXmlElement que sera para almacenar el estado raiz, despues mediante un bucle pasaremos por cada uno de los elementos de estado que hayamos informado en el archivo. En el bucle tenemos un condicional donde verificamos si el valor almacenado en la vuelta es igual al informado en idEstado. En caso de cumplirse la condicion asigna el valor a pEstadoRaiz, con esto ya tenemos el estado de nuestro juego asignado, pasemos al siguiente bloque:

	TiXmlElement* pTexturaRaiz = 0;
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == std::string("texturas"))
			pTexturaRaiz = e;
	}
	texturasParse(pTexturaRaiz, pIdtexturas);
Anuncios

Este es un caso similar al anterior pero lo usaremos para buscar todos los tags que representan a las texturas de nuestros objetos. Hay un bucle muy similar al anterior y dentro un condicional donde verificamos si el valor de nuestra vuelta es igual a «texturas» donde en caso de ser verdadero se lo asignamos a pTexturaRaiz. con este valor y el id de la textura (pIdtexturas) se lo pasamos a texturasParse. De esta funcion hablaremos un poco mas adelante, pasemos al siguiente bloque:

	TiXmlElement* pObjetoRaiz = 0;
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		if (e->Value() == std::string("objetos"))
			pObjetoRaiz = e;
	}
	objetosParse(pObjetoRaiz, pObjetos);
Anuncios

Seguimos teniendo una similitud con los segmentos anteriores pero esta orientado a los objetos. Primero crearemos un objeto para obtener los tags de objetos. Volvemos a usar un bucle para pasar por todos los elementos y el condicional esta vez verifica si el valor es igual a «objetos». El valor lo almacenamos en el objeto que creamos y al igual que en el bloque anterior llamamos a la funcion objetosParse, al igual que en la funcion anterior hablaremos mas adelante, y le pasamos el dato que obtuvimos recien y el que informamos al comienzo. Por ultimo, devolvemos un true para informar que todo se realizo correctamente en la funcion. Con esto tenemos la funcion que se encarga de analizar el archivo XML.

Anuncios

Continuemos con la siguiente funcion, veamos su codigo:

void EstadoParser::texturasParse(TiXmlElement* pEstadoRaiz,
	std::vector<std::string>* pIdtexturas) {
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		std::string atNombreArch = e->Attribute("archivo");
		std::string idAtributo = e->Attribute("id");
		pIdtexturas->push_back(idAtributo);
		Elmanejador::instanciar()->cargar(atNombreArch, idAtributo,
			Eljuego::instanciar()->getRenderer());
	}
}
Anuncios

Esta funcion recibe dos datos, el primero sera el estado obtenido del archivo y el vector o coleccion con todos los ids de las texturas. Dentro tenemos un bucle donde pasaremos por todos los elementos de las texturas, mientras se ejecuta el bucle crearemos dos variables:

  • atNombreArch: almacenaremos el valor del atributo identificado como «archivo», el cual contiene el nombre del archivo en el XML
  • idAtributo: almacenara el id correspondiente al objeto que estara vinculado.
Anuncios

Lo siguiente sera agregar el id obtenido del archivo dentro de nuestra coleccion llamada pIdtexturas. Para finalmente hacer una llamada a la funcion cargar para generar la textura correspondiente. Primero pasamos el archivo para la textura, luego el id del objeto que representa y por ultima la renderizacion tal como vinimos viendo hasta ahora pero con la diferencia que los datos son extraidos desde el archivo y no de forma literal como lo hicimos hasta ahora.

Anuncios

Con esto concluimos con la segunda funcion, pasemos a ver la ultima funcion:

void EstadoParser::objetosParse(TiXmlElement* pEstadoRaiz,
	std::vector<ObjetoJuego*>* pObjetos) {
	for (TiXmlElement* e = pEstadoRaiz->FirstChildElement(); e != NULL;
		e = e->NextSiblingElement()) {
		int x, y, ancho, alto, numFrames, idCallback, velocAnim;
		std::string idTextura;
		e->Attribute("x", &x);
		e->Attribute("y", &y);
		e->Attribute("ancho", &ancho);
		e->Attribute("alto", &alto);
		e->Attribute("numFrames", &numFrames);
		e->Attribute("idCallback", &idCallback);
		e->Attribute("velocAnim", &velocAnim);
		idTextura = e->Attribute("idTextura");
		ObjetoJuego* pObjetoJuego = Elfabricante::instanciar()->
			crear(e->Attribute("tipo"));
		pObjetoJuego->cargar(new CargadorParams(x, y, ancho, alto,
			idTextura, numFrames, idCallback, velocAnim));
		pObjetos->push_back(pObjetoJuego);
	}
}
Anuncios
Anuncios

Al igual que vimos anteriormente tenemos un bucle que pasara por todos los elementos relacionados a los objetos. En el cuerpo del bucle crearemos unas variables de tipo int para almacenar todos los valores pasados en el archivo. Y la ultima es una de tipo string para almacenar el id de la textura del objeto. Lo siguiente sera usar Attribute para obtener cada dato. En cada uno, primero pasamos el identificador que asignamos en el archivo XML, recuerden que el valor entre comillas debe coincidir con este en el archivo, y luego usaremos las variables que declaramos anteriormente para almacenar el valor obtenido. Todos trabajan de forma similar con excepcion de idTextura donde se lo asignaremos de una forma distinta. Luego crearemos un objeto de tipo ObjetoJuego pero para crearlo usaremos a la Fabrica de objetos, y a la funcion crear le pasamos el valor de tipo del archivo XML. Despues procedemos a cargar y pasamos todos los datos obtenidos y finalmente agregar en pObjetos todo lo hecho anteriormente en pObjetoJuego.

Anuncios

Con esto tenemos la clase que se encargara no solamente de abrir el archivo, establecer la ubicacion de nuestro programa para luego extraer la informacion que pasamos y finalmente asignarla a variables para luego utilizarlo por medio de todos las funciones que utilizamos anteriormente, en el proximo post ya lo veremos en accion.

Anuncios

En resumen, hoy hemos visto como cargar datos externos, hemos creado el archivo de origen, asi como la clase encargada de esto, hemos agregado las funciones que se encargaran de obtener la informacion y asignarla a las variables pertinentes tanto para las texturas como los objetos, tambien hemos visto como al final de la funcion de los objetos procede a crear cada uno de ellos como lo haciamos anteriormente.

Pasar datos desde un archivo / 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