Bienvenidos sean a este post, hoy mostraremos todo lo realizado anteriormente.
Hasta el post anterior establecimos todo lo necesario para poder manipular, decodificar y descomprimir a nuestro mapa o nivel. Retomemos nuestro proyecto Juego y vayamos a modificarlo, 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.
Antes de empezar con las modificaciones en la clase encargada de esto, debemos realizar algunos cambios en otras clases anteriores. El primer cambio lo haremos en la clase Juego y para ello, iremos a su archivo de encabezado y en la parte privada agregaremos estas variables:
int m_altoJuego;
int m_anchoJuego;
Estas dos variables seran para almacenar el ancho y alto de la ventana de nuestro juego. La siguiente modificacion es en la parte publica donde agregaremos los siguientes prototipos:
int getAltoJuego() const;
int getAnchoJuego() const;
Estas son las dos funciones para obtener los valores almacenados en las variables anteriores. Con esto establecido, vamos a trabajar en Juego.cpp. Primero iremos a la funcion iniciar y al comienzo de esta agregaremos las siguientes lineas:
m_altoJuego = alto;
m_anchoJuego = ancho;
Estas seran para iniciar a estas variables que muy pronto utilizaremos. Lo siguiente sera definir los prototipos que declaramos anteriormente:
int Juego::getAltoJuego() const { return m_altoJuego; }
int Juego::getAnchoJuego() const { return m_anchoJuego; }
Como mencionamos anteriormente, son funciones getter de las nuevas variables. Con esto terminamos con la clase Juego. La siguiente modificacion es en ManejarTexturas, para ello primero iremos al archivo de enncabezado y en la parte publica agregaremos lo siguiente:
void dibujar_patron(std::string, int, int, float, float, float, float, int,
int, SDL_Renderer*);
Esta funcion se encargara de dibujjar a los patrones desde el archivo, en este caso nuestro mapa. Vayamos a ManejarTexturas.cpp y definamos a esta funcion:
void ManejarTexturas::dibujar_patron(std::string id, int margen,
int espaciado, float x, float y, float ancho, float alto, int filaActual,
int frameActual, SDL_Renderer* pRenderer) {
SDL_FRect orgRect;
SDL_FRect dstRect;
orgRect.x = margen + (espaciado + ancho) * frameActual;
orgRect.y = margen + (espaciado + ancho) * filaActual;
orgRect.w = dstRect.w = ancho;
orgRect.h = dstRect.h = alto;
dstRect.x = x;
dstRect.y = y;
SDL_RenderTextureRotated(pRenderer, m_mapaTextura[id],
&orgRect, &dstRect,
0, 0, SDL_FLIP_NONE);
}
Esta funcion sera similar a las vistas anteriormente pero con algunos datos mas. El primero es el id de la imagen, el segundo es para el margen del conjunto de patrones, el tercero sera para el espaciado entre los patrones. Estas son las nuevas, las siguientes son las utilizadas en las otras funciones que son para ubicar y establecer las dimensiones de la imagen. Pero tambien tenemos dos argumentos «nuevos» aunque si bien representa al frame actual de la imagen tambien pasamos en que fila se encuentran en el mapa. Y el ultimo, es el lugar donde lo renderizaremos. Luego estableceremos los valores para las distintas variables en cada objeto.
La mas compleja son el calculo de los ejes X e Y para el rectangulo de origen. Para esto usaremos a margen y espaciado junto con el ancho y su ubicacion actual en el mapa. El ancho y el alto de estos los asignaremos con el valor recibido, y los valores de los ejes X e Y del rectangulo de destino seran los recibidos. La ultima funcion sera la encargada de mostrar todo lo que configuramos anteriormente, pero esta tiene la posibilidad de poder invertir la imagen cuando sea necesario.
Con todo esto realizado, nuestro siguiente paso es ir a CapaPatron.h y agregaremos la siguiente linea al inicio:
#include "Juego.h"
Con la inclusion de nuestra libreria, pasemos a la siguiente modificacion. Para ello, iremos a CapaPatron.cpp y modificaremos al constructor de la siguiente manera:
CapaPatron::CapaPatron(int tamPatron, const std::vector<ConjuntoPatron> &conjuntos):
m_tamPatron(tamPatron),m_conjuntos(conjuntos),
m_posicion(0,0),m_velocidad(0,0) {
m_numColumnas = (Eljuego::instanciar()->getAnchoJuego() / m_tamPatron);
m_numFilas = (Eljuego::instanciar()->getAltoJuego() / m_tamPatron);
}
En esta nueva, agregamos el inicio de la posicion y velocidad. Asi como tambien la del numero de columnas y filtas, para lo cual usaremos las funciones getter que agregamos anteriormente para obtener el ancho y alto del juego. Estos lo dividiremos por el tamaño del patron y sabremos cuantos disponemos. Lo siguiente sera modificar a actualizar de la siguiente manera:
void CapaPatron::actualizar(){
m_posicion += m_velocidad;
}
Con esta modificaremos la posicion en base a la velocidad, pasemos a modificar a renderizar de la siguiente manera:
void CapaPatron::renderizar(){
int x, y, x2, y2 = 0;
x = m_posicion.getX() / m_tamPatron;
y = m_posicion.getY() / m_tamPatron;
x2 = int(m_posicion.getX()) % m_tamPatron;
y2 = int(m_posicion.getY()) % m_tamPatron;
for (int i = 0; i < m_numFilas; i++) {
for (int j = 0; j < m_numColumnas; j++) {
int id = m_idPatron[i][j + x];
if (id == 0) continue;
ConjuntoPatron conjuntos = getConjuntoPorId(id);
id--;
Elmanejador::instanciar()->dibujar_patron(conjuntos.nombre, 2, 2,
(j * m_tamPatron) - x2, (i * m_tamPatron) - y2, m_tamPatron,
m_tamPatron,
(id - (conjuntos.primerCuadric - 1)) / conjuntos.numColumnas,
(id - (conjuntos.primerCuadric - 1)) % conjuntos.numColumnas,
Eljuego::instanciar()->getRenderer());
}
}
}
Primero definimos las posiciones de los ejes X e Y, al inicio del patron y al final del mismo. Con estos valores obtenidos, usaremos dos bucles para pasar por las dos dimensiones de m_idPatron. Con el valor del id obtenido, por el momento ignoraremos al posicionamiento, chequeamos si es igual a cero. Si es asi, usamos a continue para que ignore todo lo que sigue y pase a la siguiente vuelta. Pero por que hacemos esto? Simple, el id con el valor de cero significa que no hay ningun patron y por tanto debe ignorarlo. En caso de no cumplirse, generamos una variable para obtener el conjunto mediante el id obtenido anteriormente. Pero sobre esta funcion, hablaremos en un momento cuando la definamos. Luego decrementamos a id y luego llamamos a la funcion que definimos en ManejarTexturas.
Observen como pasamos los datos, el primero es el nombre del patron como id, el margen y el espaciado, en este post cuando lo creamos en la imagen que usamos de conjunto de patron tenia esos valores de espaciado y margen. Con cada calculo, vamos pasando los distintos valores que necesitamos para la funcion. Pero nos falta una definicion mas, para ello en este mismo archivo modificaremos a getConjuntoPorId de la siguiente manera:
ConjuntoPatron CapaPatron::getConjuntoPorId(int id) {
for (int i = 0; i < m_conjuntos.size(); i++) {
if (i + 1 <= m_conjuntos.size() - 1) {
if (id >= m_conjuntos[i].primerCuadric &&
id <= m_conjuntos[i + 1].primerCuadric) {
return m_conjuntos[i];
}
}
else {
return m_conjuntos[i];
}
}
std::cout << "No se encontro conjunto, devuelvo uno vacio\n";
ConjuntoPatron c;
return c;
}
Aqui usaremos un bucle donde pasaremos por todos los conjuntos. Tenemos un condicional, donde si se cumple llama a otro condicional. En cualquiera de los dos casos nos devolvera el conjunto en esa posicion. Si no se cumple ninguna de las dos, el bucle no devolvera nada y podemos simbolizar como que no se encontro. Por esa razon, mostramos un mensaje indicando esto. Creamos un objeto vacio y devolvemos a este. Con esto, podemos volver a la funcion anterior, renderizar, y tomemos este segmento:
Elmanejador::instanciar()->dibujar_patron(conjuntos.nombre, 2, 2,
(j * m_tamPatron) - x2, (i * m_tamPatron) - y2, m_tamPatron,
m_tamPatron,
(id - (conjuntos.primerCuadric - 1)) / conjuntos.numColumnas,
(id - (conjuntos.primerCuadric - 1)) % conjuntos.numColumnas,
Eljuego::instanciar()->getRenderer());
Lo modificaremos de la siguiente manera:
Elmanejador::instanciar()->dibujar_patron(conjuntos.nombre,
conjuntos.margen, conjuntos.espaciado,
(j * m_tamPatron) - x2, (i * m_tamPatron) - y2, m_tamPatron,
m_tamPatron,
(id - (conjuntos.primerCuadric - 1)) / conjuntos.numColumnas,
(id - (conjuntos.primerCuadric - 1)) % conjuntos.numColumnas,
Eljuego::instanciar()->getRenderer());
Hicimos un cambio simple y solitario, al momento de pasa el espaciado y margen. Antes lo pasabamos de manera manual, pero ahora en base al conjunto obtenido mediante la ultima funcion. Como ahora, tenemos ese objeto con los datos siempre es preferible obtenerlo desde donde lo establecimos. El resto, no sufre ningun cambio.
Con esto, tenemos casi todo terminado pero solo nos falta un par de cambios mas. Estos cambios los haremos en EstadoJugar, lo primero que haremos es ir a su archivo de encabezado y al inicio agregaremos esta linea:
#include "ParserNivel.h"
Con esta libreria agregada, en la parte privada de la clase agregaremos esta linea:
Nivel* pNivel;
Este sera el contenedor de nuestro nivel. Nuestra siguiente modificacion sera en el archivo .cpp, cambiemos la definicion de enIngreso de la siguiente manera:
bool EstadoJugar::enIngreso() {
ParserNivel parserNivel;
pNivel = parserNivel.parseNivel("assets/mapa01.tmx");
OutputDebugStringA("Ingresando al Estado de jugar\n");
return true;
}
Basicamente, eliminamos todo lo relacionado a mostrar los distintos elementos para crear al nivel. Para ello, generamos al encargado de crear al nivel (ParserNivel) y luego utilizamos la funcion parserNivel para que el archivo que contiene el mapa con todas las imagenes. Sobre este archivo hablaremos en un poco mas adelante. El resultado de todo lo obtenido de este archivo lo asignamos a pNivel. Nos resta un par de modificaciones mas en este archivo. La primera es en la funcion de renderizar, la cual modificaremos de la siguiente manera:
void EstadoJugar::renderizar() {
pNivel->renderizar();
}
Simplemente, hacemos que el renderizado pase a ser manejado por el renderizador de la clase Nivel. Y la ultima modificacion es en la funcion actualizar donde comentaremos, no lo borren al siguiente bloque de codigo:
if (chekaColision(dynamic_cast<SDLObjetoJuego*>(m_objetosJuego[0]),
dynamic_cast<SDLObjetoJuego*>(m_objetosJuego[1]))) {
Eljuego::instanciar()->getEstadoMaquina()->
pushEstado(new EstadoFinal());
}
Este condicional era uno que verifica la colision entre los distintos elementos del juego pero como por ahora no tenemos ninguno, les recomiendo comentarlos para evitar algunos inconvenientes. Y no lo borren, porque lo necesitaremos en un futuro, o por lo menos cuando volvamos a poner los elementos en pantalla.
Ahora debemos agregar a nuestro mapa o nivel. Para ello, deben usar el mapa que generamos en este post, sino lo poseen les dejo un link para descargarlo:
Ya sea que posean al proyecto que creamos para el mapa o bien lo hayan descargado, deben copiar los siguientes archivos en el directorio assets dentro de la carpeta de nuestro proyecto Juego:
- mapa01.tmx
- blocks1.png
- blocks2.png
Solo nos falta una ultima modificacion, para ello deben abrir a mapa01.tmx con e bloc de notas, para hacerlo mas simple, y deben buscar las siguientes lineas:
<image source="blocks1.png" width="614" height="376"/>
...
<image source="blocks2.png" width="614" height="376"/>
Y las modificaremos de la siguiente manera:
<image source="assets/blocks1.png" width="614" height="376"/>
...
<image source="assets/blocks2.png" width="614" height="376"/>
Con esto realizado, podemos compilarlo y probar como funciona nuestro proyecto ahora. Veamos esto mediante el siguiente video
Si lograron lo mismo que se ve en el video, FELICITACIONES!!!! Ya tenemos a nuestro nivel o escenario integrado a nuestro juego. Pero esto no se termina aca porque vamos a hacer un par de modificaciones mas.
Para realizar estas modificaciones debemos volver al programa Tiled, al cual instalamos y explicamos en este post, para modificar nuestro mapa. Abran el programa y el proyecto que descargaron anteriormente, no el archivo .tmx sino a Juego.tiled-project, para comenzar a modificar. Con nuestro proyecto abierto debemos ir a la opcion Mapa, y luego a Redimensiona Mapa. Esto nos abrira la siguiente ventana

En esta nueva ventana, debemos modificar el Ancho de 20 patrones a 60 patrones, como se ve en la siguiente imagen

En la imagen pueden ver como se estiro la imagen pero nuestra imagen anterior sigue solo en la parte original, la nueva estara completamente en blanco. Para completar lo faltante, vamos a utilizar el siguiente video
Si editaron el proyecto que descargaron originalmente, recuerden modificar las ubicaciones de las imagenes como hicimos anteriormente. Porque no las encontrara y mostrara una pantalla en negro. Copian el nuevo archivo .tmx en la carpeta assets para reemplazar al anterior. Con esto realizado, tenemos que realizar un par de cambios en nuestro codigo.
El primer cambio sera en la funcion actualizar de la clase CapaPatron, la cual modificaremos de la siguiente manera:
void CapaPatron::actualizar() {
m_posicion += m_velocidad;
m_velocidad.setX(1);
}
Si lo comparan con el codigo anterior, agregamos el incremento del eje X para poder desplazar a nuestro nuevo escenario. Solo nos resta una modificacion mas, para ello debemos ir a EstadoJugar.cpp y modificaremos a la funcion actualizar de la siguiente manera:
void EstadoJugar::actualizar() {
if (Controles::instanciar()->teclaPresionada(SDL_SCANCODE_ESCAPE))
Eljuego::instanciar()->getEstadoMaquina()->
pushEstado(new EstadoPausa());
/*if (chekaColision(dynamic_cast<SDLObjetoJuego*>(m_objetosJuego[0]),
dynamic_cast<SDLObjetoJuego*>(m_objetosJuego[1]))) {
Eljuego::instanciar()->getEstadoMaquina()->
pushEstado(new EstadoFinal());
}*/
pNivel->actualizar();
}
Mantenemos a la tecla ESC para pausar al juego, pero agregamos el llamado a actualizar pero del nivel. Y comentamos el chequeo de colisiones, porque como mencionamos antes la reusaremos cuando agreguemos los elementos. Con esta ultima modificacion, podemos ver como trabaja mediante el siguiente video
En el video podemos ver como ahora nuestro nuevo mapa se va desplazando automaticamente y cuando presionamos ESC se detiene pero continua cuando retomamos y podemos volver al menu principal.
Si se lo preguntan, toda la magia es realizada en la funcion renderizar en la clase CapaPatron. Mas exactamente en este segmento:
x = m_posicion.getX() / m_tamPatron;
y = m_posicion.getY() / m_tamPatron;
x2 = int(m_posicion.getX()) % m_tamPatron;
y2 = int(m_posicion.getY()) % m_tamPatron;
Las primeras dos lineas se encargan basicamente de ubicarnos, y las otras dos se encargan de controlar el movimiento de una manera sutil y fluida, tambien nos mantendra nuestra ventana en la parte en la seccion del mapa donde nos movilizamos.
Un ultimo detalle, si dejan correr al mapa hasta al final nos devolvera un error pero solo por el hecho de que no tenemos como manejar eso pero muy pronto lo solucionaremos.
En resumen, hoy hemos visto como mostrar al mapa, implementamos todo lo visto en posts anteriores, modificamos al mapa para poder ser accedido, asi como tambien hemos modificado su tamaño, para ver una nueva implementacion. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:
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





