¿Aún no te has enterado que sucede? Pues dejame decirte que estas algo atrasado, la 6º entrega de estos grandiosos tutoriales ya ve la luz y tu ahí sin saber porque aparecen estos tutoriales. Vamos, no pierdas más el tiempo y enterate de qué sucede, de seguro que es de todo tu interés.
En la 5ª entrega aprendimos a manejar texturas. Eso nos permitió darle un bonito aspecto a nuestro button tester.
Ahora vamos a dar un paso más en el manejo de las texturas, viendo primero como se manejan los tiles en general y luego como aplicarlos para manejar fuentes bitmap.
Según la definición de un diccionario de Ingles-Español la definición de tile es; "Baldosa", "Losa", "Azulejo". Pero ¿qué tiene que ver esto con las texturas y con los juegos?.
La palabra Tile tiene otro significado diferente cuando nos adentramos en la jerga de los programadores de videojuegos, aunque cuando entendamos su significado comprenderemos el motivo de dicho nombre.
En dicha jerga, llamaremos Tile a cada uno de los trozos de igual tamaño en los que dividimos una textura. A esa textura le llamaremos Tileset y cada uno de esos trozos o "Tiles" podrá ser usado de manera independiente en nuestro programa.
Imaginemos una gran pared formada por ladrillos del mismo tamaño pero cada uno de ellos con un dibujo diferente. Pues bien, la pared sería el TileSet y cada uno de los ladrillos sería un Tile.
Los tiles son muy utilizados en videojuegos ya que permiten la utilización de los trozos de una sola imagen como texturas independientes y acceder a ellas mediante un índice numérico.
Esto último es muy util por ejemplo para la elaboración de laberintos, juegos de plataformas o los diferentes movimientos de un sprite etc.
Veamos el ejemplo de la animación de un Pacman:
Con un tileset como el de la figura tenemos disponibles todos los movimientos posibles de nuestro amigo Pacman cuando se mueve hacia la derecha. Si queremos crear un efecto de animación solo debemos ir mostrando consecutivamente los tiles del 0 al 4 y vuelta a empezar. Teniendo acceso a los tiles mediante un índice la tarea es muy sencilla ya que se reduce a la utilización de un contador que va cambiando su valor de una manera secuencial.
Para generar los movimientos en otras direcciones bastará con girar el personaje.
Veamos el ejemplo del movimiento de Pinky, el fantasma rosa de Pacman:
En este caso los movimientos hacia el resto de las direcciones necesitan tiles diferentes por lo que utilizaremos un tileset con todas las imágenes posibles para cada una de las direcciones válidas (izquierda, arriba, derecha, abajo). Para poder acceder al tile correcto bastará con numerar las direcciones del 0 al 3 y utilizar una sencilla fórmula:
tile = direccion * 3 + frame.
Donde dirección es el número que le hemos asignado en la tabla anterior, 3 es el número total de frames que componen el movimiento en una de las direcciones y frame el frame que toca visualizar.
De esta manera, si Pinky se desplaza hacia la derecha tendremos que visualizar secuencialmente los tiles 6, 7 y 8 osea 2*3+frame.
Otro ejemplo posible es la utilización de tiles para almacenar una lista de los posibles elementos que podemos utilizar para contruir un escenario, ya sea un laberinto o un juego de plataformas.
Podemos ver un trozo del tileset utilizado en uno de las entregas de pokemon.
En este caso el contenido del tileset son cada uno de los trozos posibles que nos permitirán componer el puzzle que compone el escenario por donde nos movemos.
La utilización de dicho tileset pasará por crear una matriz para representar el escenario en su conjunto. Dicha matriz contendrá en cada celda el número de tile que se mostrará en la posición del escenario representada por ella.
Mejor veamoslo en un ejemplo. Si un trozo de escenario consiste en una zona de cesped en la que hay un arbusto en su centro, la matriz que lo representaría sería como la que sigue:
Una vez tenemos dicha matriz, mediante un sencillo bucle que recorra toda la matriz y utilizando las funciones de dibujo de tiles podremos mostrar todo el escenario.
La primera función que debemos conocer es GRRLIB_InitTileSet, que es la función que nos permite especificar los parámetros necesarios para que GRRLIB sepa manejar correctamente el tileset.
La sintáxis de la función la podemos ver abajo y básicamente consiste en especificar la anchura y altura de los tiles que contiene el tileset.
El parámetro tilestart sirve unicamente cuando utilizamos el tileset para fuentes bitmap, por lo que lo explicaremos más adelante.
void GRRLIB_InitTileSet ( GRRLIB_texImg *tex, const uint tilew, const uint
tileh, const uint tilestart )
Inicializa un tile set.
Parametros:
tex La textura que contiene los tiles.
tilew Anchura del tile en píxeles.
tileh Anchura del tile en píxeles.
tilestart Desplazamiento de la posición de comienzo (Usado en fuentes bitmap).
La siguiente función que tenemos que conocer es GRRLIB_DrawTile que es la función que nos permitirá mostrar un determinado tile de un tileset.
La función tiene una sintáxis idéntica a la utilizada por GRRLIB_DrawImg que ya vimos en la entrega anterior, pero esta vez añadiremos el parámetro frame que es un índice al tile que queremos mostrar. Los frames se numeran comenzando en 0 y en la esquina superior izquierda de la textura, continuando secuencialmente hacia la derecha y hacia abajo.
void GRRLIB_DrawTile ( const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, const f32 degrees, const f32 scaleX, const f32 scaleY, const u32 color, const int frame ) Dibuja un tile Parametros: xpos: Posición de la coordenada x de la esquina superior izquierda. ypos: Posición de la coordenada y de la esquina superior izquierda. tex: La textura que contiene el tile a mostrar. degrees: Angulo de rotación. scaleX: Especifica la coordenada x del escalado. Con -1 se invierte la textura horizontalmente. scaleY: Especifica la coordenada y del escalado. Con -1 se invierte la textura verticalmente color: Color en formato RGBA. frame: El número de tile a mostrar.
La última función relacionada con tiles es GRRLIB_DrawTileQuad y es una función que permite mostrar un tile pero de una manera alternativa a GRRLIB_DrawTile.
La sintáxis de GRRLIB_DrawTileQuad es como sigue:
void GRRLIB_DrawTileQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color, const int frame ) Dibuja un tile en un Quad Parametros pos: Array de los cuatro puntos que componen los vértices del Quad tex: La textura que contiene el tile a mostrar. color: Color en formato RGBA. frame: El número de tile a mostrar.
Como su nombre sugiere, esta función muestra un tile ajustándolo interiormente al quad que pasamos como primer parametro.
En la jerga de programación gráfica, un Quad es un polígono de cuatro lados (o cuatro vértices) que definimos por la posición de sus cuatro vértices.
El parámetro pos contiene un array de cuatro elementos de tipo guVector, que nos permitirá definir los cuatro vértices entre los cuales se mostrará el Quad.
Como hemos nombrado en ocasiones anteriores, una fuente bitmap es una fuente de letras que está definida pixel a pixel. Frente a las fuentes True Type tiene la desventaja de que es mucho más rígida en su utilización, ya que para que el resultado sea aceptable deberemos tener una fuente diferente para cada tamaño de letra. Sin embargo estas fuentes tienen la ventaja de que son mucho más rápidas a la hora de su visualización, por lo que son idóneas para su uso en juegos.
La inicialización de una fuente bitmap se realiza de la misma manera que la de un tileset, pero en este caso debemos hacer uso del último parámetro de GRRLIB_InitTileSet. Dicho parámetro indica cual es código del primer carácter que tenemos definido en nuestro TileSet. Esto es imprescindible, ya que GRRLIB utilizará el código de caracter a mostrar como índice del TileSet, por lo que si no hemos definido correctamente este parámetro el resultado será que las letras mostradas no sean las deseadas.
La función que utilizaremos para mostrar un texto con una fuente bitmap es GRRLIB_Printf que tiene el siguiente formato:
void GRRLIB_Printf ( const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, const u32 color, const f32 zoom, const char *text, ... ) Mostrar una cadena de texto Parametros xpos: Posición de la coordenada x de la esquina superior izquierda. ypos: Posición de la coordenada y de la esquina superior izquierda. tex: La textura que contiene el juego de caracteres. color: Color en formato RGBA. zoom Factor de aumento o decremento del tamaño del texto. text Texto a mostrar. ... Argumentos opcionales.
Como podéis ver una vez inicializado el tileset la utilización de este como una fuente bitmap es sencilla y pasa por la llamada GRRLIB_Printf con las coordenadas a imprimir y el texto a mostrar.
La cadena de texto que pasamos a bitmap se trata de la misma forma que la función estándar de C printf, de forma que podemos añadir parámetros adicionales para imprimir cadenas formateadas.
Si no sabéis como funciona la función printf de C, aquí os dejo un enlace que lo explica en profundidad.
http://www.cplusplus.com/reference/clibrary/cstdio/printf/
Un ejemplo de llamada a GRRLIB_Printf sería:
GRRLIB_Printf(100, 100, tex_pacfont, 0xFFFF00FF, 1, "HOLA");
Que mostraría el texto HOLA de color amarillo en las coordenadas 100,100.
Y ahora la pregunta que probablemente os esté rondando por la cabeza es ¿Dónde puedo encontrar fuentes bitmap?.
Podríamos buscarlas en internet, pero probablemente, bien no encontraríamos la que queremos, bien no será del tamaño deseado.
¿Cómo podemos entonces encontrarlas?. Muy sencillo, las podemos crear nosotros mismos a partir de fuentes truetype.
Hay bastantes programas en internet que pueden hacer exactamente eso. Yo he utilizado para crear las fuentes bitmap del ejemplo el programa Wiibuilder, de Crayon, que es uno de los coautores de GRRLIB.
Wiibuilder es un programa que, entre otras utilidades, tiene un generador de fuentes bitmap a partir de las fuentes truetype que tenemos en nuestro ordenador.
Podeís descargarlo de Wiibrew en el siguiente enlace: http://wiibrew.org/wiki/WiiBuilder
Una vez descargado, únicamente tenéis que ejecutar Wiibuilder.exe y seleccionar a la izquierda la opción "Font generator".
Ya situados en dicha opción, elegiremos en la ventana de la derecha la fuente truetype y el tamaño de letra deseado. Los valores Start y End nos permitirán elegir los caracteres que exportaremos.
En cuanto al color os recomiendo el blanco, ya que siempre podemos usar el parámetro color de GRRLIB_Printf si queremos usar un color diferente.
Una vez seleccionadas las opciones deseadas pulsaremos Generate Image que nos generará la textura de la fuente.
Debemos entonces apuntarnos las dimensiones del Tile que aparecen abajo a la derecha y recordar el número que hemos seleccionado como parámetro Start, ya que estos números los necesitaremos para los parámetros tilew, tileh y tilestart de la función GRRLIB_InitTileSet.
Con la ayuda de las funciones anteriores y con todo lo aprendido en las anteriores entregas, vamos a intentar hacer un programa de ejemplo que nos permita ver de manera aplicada el uso de tiles. El programa que he elegido como ejemplo es un simplificado pacman, en el que tenemos manejaremos a nuestro amigo con las flechas del wiimote. Los clásicos fantasmas nos perseguirán a no ser que pulsemos "+" tras lo cual entrarán en modo pánico y huirán de nuestro personaje.
Para crear nuestro programa de ejemplo utilizaremos en total siete tilesets. Cinco de ellos para el movimiento de los sprites y dos fuentes de letra bitmap que nos las incluyo ya que al ser de color blanco no se aprecian.
Los TileSet los he creado con la ayuda de GIMP, PowerPoint y Wiibuilder, pero esto ya es más una cuestión de gustos y de los programas que domine cada uno.
Una cosa que no habíamos nombrado es el cometido de la última fila del tileset de los fantasmas. Dicha fila contiene los frames necesarios para el momento que el fantasma entra en modo pánico. Esto implicará que tengamos que darle un tratamiento específico a este caso como veremos más adelante en el código.
En cuanto a las fuentes usadas, la primera es la fuente pacman que la he generado con wiibuilder a partir de una fuente truetype gratuita que me he descargado de internet, y la segunda esta generada a partir de la fuente estándar Arial Black que viene con Windows.
Todas estas texturas deben acabar en la carpeta data para que puedan ser enlazadas por el compilador.
Echándole un ojo al código comenzaremos, como en las entregas anteriores, incluyendo las bibliotecas habituales y los .h de las texturas, además de la definición de las constantes de color que ya vimos anteriormente:
#include <grrlib.h> #include <stdlib.h> #include <wiiuse/wpad.h> #include "pacman_png.h" #include "pinky_png.h" #include "blinky_png.h" #include "inky_png.h" #include "clyde_png.h" #include "pacfont_36_png.h" #include "arial_black_12_png.h" // Colores RGBA #define GRRLIB_BLACK 0x000000FF #define GRRLIB_MAROON 0x800000FF #define GRRLIB_GREEN 0x008000FF #define GRRLIB_OLIVE 0x808000FF #define GRRLIB_NAVY 0x000080FF #define GRRLIB_PURPLE 0x800080FF #define GRRLIB_TEAL 0x008080FF #define GRRLIB_GRAY 0x808080FF #define GRRLIB_SILVER 0xC0C0C0FF #define GRRLIB_RED 0xFF0000FF #define GRRLIB_LIME 0x00FF00FF #define GRRLIB_YELLOW 0xFFFF00FF #define GRRLIB_BLUE 0x0000FFFF #define GRRLIB_FUCHSIA 0xFF00FFFF #define GRRLIB_AQUA 0x00FFFFFF #define GRRLIB_WHITE 0xFFFFFFFF #define GRRLIB_LBLUE 0x5050FFFF
Inmediatamente después declararemos una serie de constantes adicionales que nos serán de utilidad a lo largo de nuestro código:
#define _TITLE "pacman" #define _TEXT2 "pulsa + para modo panico" #define _FONT_WIDTH_PAC 66 #define _FONT_HEIGHT_PAC 49 #define _FONT_WIDTH_ARIAL 16 #define _FONT_HEIGHT_ARIAL 23 #define _IZQUIERDA 0 #define _ARRIBA 1 #define _DERECHA 2 #define _ABAJO 3
Las constantes anteriores, como ya veremos después, contienen los textos a mostrar, los valores de anchura y altura de caracter de las fuentes utilizadas y la codificación de las direcciones de movimiento que nos permitirán acceder al tile correcto mediante una fórmula.
Inmediatamente después nos encontramos la definición de una serie de variables utilizadas por el programa y que iremos explicando según vayamos viendo el código.
int velpacman = 4; int velghost = 4; int dirghost[4] = {_DERECHA,_DERECHA,_DERECHA,_DERECHA}; int frameghost = 0; int xghost[4] = {0,64,128,192}; int yghost[4] = {100,100,100,100}; int framepacman = 0; int xpacman = 256; int ypacman = 100; int ciclos = 0; int dirpacman = _DERECHA; GRRLIB_texImg* tex_ghost[4]; GRRLIB_texImg* tex_pacman; bool panicmode = 0; int resx = 640; int resy=520;
Vamos a pasar al main para luego ver la necesidad de la creación de las funciones gestiona_pacman y gestiona_fantasma a medida que nos adentramos en él.
Como siempre, lo primero que hacemos en el main es inicializar GRRLIB y WPAD.
// Inicializar gráficos GRRLIB_Init(); // Inicializar wiimotes WPAD_Init(); WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR); WPAD_SetVRes(WPAD_CHAN_ALL, resx, resy);
En las variables resx y resy están las resoluciones utilizadas por mí es decir 640x520. Si estáis utilizando un modo de 480 puntos modificad resy.
Seguidamente utilizamos LoadTexture para cargar las texturas en variables de textura.
tex_ghost[0] = GRRLIB_LoadTexture(pinky_png); tex_ghost[1] = GRRLIB_LoadTexture(blinky_png); tex_ghost[2] = GRRLIB_LoadTexture(inky_png); tex_ghost[3] = GRRLIB_LoadTexture(clyde_png); tex_pacman = GRRLIB_LoadTexture(pacman_png); GRRLIB_texImg* tex_pacfont = GRRLIB_LoadTexture(pacfont_36_png); GRRLIB_texImg* tex_arial_black = GRRLIB_LoadTexture(arial_black_12_png);
En el caso concreto de los fantasmas utilizaremos un array de texturas que nos permitirá tratar a todos los fantasmas de un modo genérico.
Una vez tenemos las texturas en memoria, llamamos a GRRLIB_InitTileSet para inicializar los Tilset.
GRRLIB_InitTileSet (tex_pacman, 64, 64, 0); for (i=0; i<4; i++) GRRLIB_InitTileSet (tex_ghost[i], 64, 64, 0); GRRLIB_InitTileSet (tex_pacfont, _FONT_WIDTH_PAC, _FONT_HEIGHT_PAC, 32); GRRLIB_InitTileSet (tex_arial_black, _FONT_WIDTH_ARIAL, _FONT_HEIGHT_ARIAL, 32);
Tanto en el caso de Pacman como en el de los fantasmas son tiles de 64x64, mientras que para las fuentes usaremos los tamaños que nos había proporcionado Wiibuilder, comenzando en ambos casos en el caracter 32 que es el correspondiente al código del espacio.
A partir de aquí entraremos ya en el bucle principal comenzando como siempre por la lectura de los wiimotes.
while(1){ WPAD_ScanPads(); // Leer mandos
Seguidamente utilizaremos la función GRRLIB_Printf para mostrar el título y la nota aclaratoria sobre como hacer que los fantasmas entren en modo pánico.
GRRLIB_Printf((640-(_FONT_WIDTH_PAC*strlen(_TITLE)))/2, 360, tex_pacfont, GRRLIB_YELLOW, 1, _TITLE); GRRLIB_Printf((640-(_FONT_WIDTH_ARIAL*strlen(_TEXT2)))/2, 440, tex_arial_black, GRRLIB_PURPLE, 1, _TEXT2);
Acto seguido, comprobamos las teclas pulsadas en el wiimote 0 comprobando lo siguiente:
Las líneas que van a continuación llamarán a las funciones que muestran los personajes y gestionan el comportamiento de los mismos. Dichas funciones son gestiona_pacman() y gestiona_fantasma().
La función gestiona_fantasma la hemos declarado como función para poder reutilizarla dentro de la llamada al bucle, ya que el comportamiento de todos los fantasmas es idéntico y se puede generalizar. He seguido el mismo criterio con la función gestiona_pacman por motivos de claridad de código.
gestiona_pacman (); int i; for (i=0; i<4; i++){ gestiona_fantasma (i);
Terminaremos de ver el cuerpo del main y al final entraremos en detalle en estas dos funciones.
El código siguiente gestiona las variables que controlan el frame que toca visualizar, tanto en el caso del pacman como en el de los fantasmas.
ciclos++; if (ciclos >= 3) { ciclos = 0; framepacman++; if (framepacman > 4) framepacman = 0; frameghost++; if (frameghost > 2) frameghost = 0; }
Para comprender el sentido de dicho código, lo primero que tenemos que tener en cuenta es que la frecuencia de refresco de la pantalla es 50Hz en el caso de PAL y 60Hz en el caso de NTSC.
Una de las cosas que hace la función GRRLIB_Render cada vez que la llamamos al final de un bucle es sincronizarse con la frecuencia de refresco de la pantalla. Esto trae como efecto secundario que nuestro bucle se ejecute como máximo, 50 veces por segundo en el caso de PAL y 60 veces por segundo en el caso de NTSC.
Esto nos impone una limitación de velocidad, pero por otro lado nos provee de un medio de sincronizar los movimientos de nuestros personajes.
Recordemos que nuestro pacman debe mostrar consecutivamente los 5 frames que componen el movimiento. Esto supone, en el caso de que estemos ejecutando nuestro programa en un sistema PAL, que compretaremos un ciclo cada décima de segundo (5 frames / 50 Hz = 0.1 segundos). Esto es demasiado rápido para conseguir el efecto deseado. Debemos pues desarrollar un método que ralentice dicho ciclo.
Un método bastante sencillo es implementar un contador, que en nuestro ejemplo he llamado ciclo, que se vaya incrementando hasta un número de ciclos determinado que cuando es alcanzado incrementa nuestro contador de frames y vuelve a ponerse a 0.
En nuestro código incrementamos dicho contador hasta un valor de 3, lo que multiplica por tres el tiempo que tarda en ejecutarse un ciclo completo, traduciendose ahora en un ciclo de 0.33 segundos que produce un efecto más natural.
Como hemos dicho antes, cada vez que la variable ciclo alcanza el valor 3 incrementamos el contador de frames, tanto del pacman como de los fantasmas, teniendo en cuenta, que cuando alcanzamos el valor del frame máximo debemos inicializar el contador de frames a 0.
En el caso del pacman el número de frames es 5 y en el de los fantasmas es 3, por lo que las comparaciones framepacman > 4 y frameghost > 2 nos serviran para saber cuando debemos inicializar dichos contadores de frames.
Ya tenemos pues dos variables (framepacman y frameghost) que nos van a indicar el frame que toca mostrar de entre los posibles de cada personaje. Vamos ahora a ver en detalle el código de las funciones gestiona_pacman y gestiona_fantasma.
Empezaremos por gestiona_pacman.
void gestiona_pacman (){ if (dirpacman == _DERECHA) { xpacman+=velpacman; GRRLIB_DrawTile (xpacman, ypacman, tex_pacman, 0, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _IZQUIERDA) { xpacman-=velpacman; GRRLIB_DrawTile (xpacman+64, ypacman+64, tex_pacman, 180, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _ARRIBA) { ypacman-=velpacman; GRRLIB_DrawTile (xpacman, ypacman+64, tex_pacman, 270, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _ABAJO) { ypacman+=velpacman; GRRLIB_DrawTile (xpacman+64, ypacman, tex_pacman, 90, 1, 1, GRRLIB_WHITE, framepacman); } if (xpacman > resx) xpacman = 0; if (xpacman < 0) xpacman = resx; if (ypacman < 0) ypacman = resy; if (ypacman > resy) ypacman = 0; }
Los primeros cuatro bloques if comprueban la dirección del pacman guardada en la variable dirpacman, incrementando o disminuyendo las variables xpacman e ypacman, que contienen la posición actual del pacman, segun sea la dirección del movimiento.
En dicha función se llama también a la función GRRLIB_DrawTile para mostrar el frame que toca mostrar de nuestro pacman, que como hemos visto antes tenemos su índice guardado en la variable framepacman.
Giraremos el frame usando el parámetro correspondiente de GRRLIB_DrawTile según la dirección:
Además debemos aplicar una corrección en el desplazamiento, debido a que el centro de giro que utiliza GRRLIB_DrawTile es la esquina superior izquierda en lugar del centro del Tile. Esto último es el motivo de que sumemos 64 a las variables xpacman e ypacman dependiendo del giro aplicado.
Los cuatro últimos if de nuestra función nos servirán para hacer que pacman aparezca por el extremo contrario cuando se salga de la pantalla.
Veamos ahora la función gestiona_fantasma.
La idea es gestionar todos los fantasmas de la misma manera, por lo que a todas las variables relacionadas con el estado de los fantasmas les vamos a dar el formato de un array de 4 elementos, de tal manera que podamos referirnos a los fantasmas por un índice de 0 a 3.
Las variables a las que estoy haciendo referencia están declaradas en el código del programa de la siguiente manera:
int dirghost[4] = {_DERECHA,_DERECHA,_DERECHA,_DERECHA}; int xghost[4] = {0,64,128,192}; int yghost[4] = {100,100,100,100}; iGRRLIB_texImg* tex_ghost[4];
Es decir posición (xghost e yghost), dirección (dirghost) y tileset (tex_ghost).
En la función gestiona_fantasma gestionamos el comportamiento del fantasma indicado por el parámetro i.
void gestiona_fantasma (int i){ // compara la posición del fantasma con el pacman // y elige la dirección correcta para perseguirlo // o huir en caso de modo pánico int dirx = -1; int diry = -1; if (panicmode){ if ((xghost[i] > 64) && (xghost[i] < xpacman)) dirx = _IZQUIERDA; if ((xghost[i] < resx-64) && (xghost[i] > xpacman)) dirx = _DERECHA; if ((yghost[i] > 64) && (yghost[i] < ypacman)) diry = _ARRIBA; if ((yghost[i] < resy-64) && (yghost[i] > ypacman)) diry = _ABAJO; } else { if (xghost[i] < xpacman) dirx = _DERECHA; if (xghost[i] > xpacman) dirx = _IZQUIERDA; if (yghost[i] < ypacman) diry = _ABAJO; if (yghost[i] > ypacman) diry = _ARRIBA; } // tres posibilidades // 0: no cambia de dirección // 1: cambia la dirección del eje x // 2: cambia la dirección del eje y int newdir = rand() % 3; if (newdir==1) if (dirx >= 0) dirghost[i]= dirx; if (newdir==2) if (diry >= 0) dirghost[i]= diry; // finalmente movemos el fantasma if (dirghost[i] == _DERECHA) xghost[i]+=velghost; if (dirghost[i] == _IZQUIERDA) xghost[i]-=velghost; if (dirghost[i] == _ARRIBA) yghost[i]-=velghost; if (dirghost[i] == _ABAJO) yghost[i]+=velghost; if (!panicmode) GRRLIB_DrawTile (xghost[i], yghost[i], tex_ghost[i], 0, 1, 1, GRRLIB_WHITE, dirghost[i]*3+frameghost); else GRRLIB_DrawTile (xghost[i], yghost[i], tex_ghost[i], 0, 1, 1, GRRLIB_WHITE, 4*3+frameghost); if (xghost[i] > resx) xghost[i] = 0; if (xghost[i] < 0) xghost[i] = resx; if (yghost[i] < 0) yghost[i] = resy; if (yghost[i] > resy) yghost[i] = 0; }
En el cuerpo de la función lo primero que haremos será elegir la dirección correcta que tendrá que adoptar el fantasma para huir o perseguir al pacman dependiendo de si está o no en modo pánico. Para ello comparamos la posición del fantasma respecto del pacman para los ejes x e y almacenando la dirección a la que debería ir el fantasma para huir o perseguir a pacman según el modo. Haremos esto último para los ejes x e y.
Almacenamos la dirección deseada para cada eje en las variables dix y diry y como no podemos ir en diagonal echaremos a suerte entre tres posibilidades, cambiar hacia dirx hacia diry o no modificar la dirección anterior.
Para el factor aleatorio utilizaremos la función de C rand() que nos devuelve un valor aleatorio entre 0 y 32767. Como nosotros solo queremos 3 posibilidades utilizaremos el resto de dividir rand() por 3 que dará siempre como resutado un número aleatorio entre 0 y 2.
Para calcular el resto de la división utilizaremos el operador %.
Seguidamente mostraremos el fantasma con la función GRRLIB_DrawTile.
En el caso de que estemos en modo pánico el tile deberá seleccionarse entre uno de los de la última fila (la número 4 si empezamos a numerarlas por 0), por lo que utilizaremos la fórmula 4*3+frameghost.
En caso de no estar en modo pánico utilizaremos la variable dirección para seleccionar la fila adecuada (entre 0 y 3) por lo que la fórmula quedaría: dirghost[i]*3+frameghost.
Finalmente y al igual que con pacman compararemos con el borde de la pantalla para hacer que cuando salgan por un extremo aparezcan por el otro.
#include <grrlib.h> #include <stdlib.h> #include <wiiuse/wpad.h> #include "pacman_png.h" #include "pinky_png.h" #include "blinky_png.h" #include "inky_png.h" #include "clyde_png.h" #include "pacfont_36_png.h" #include "arial_black_12_png.h" // Colores RGBA #define GRRLIB_BLACK 0x000000FF #define GRRLIB_MAROON 0x800000FF #define GRRLIB_GREEN 0x008000FF #define GRRLIB_OLIVE 0x808000FF #define GRRLIB_NAVY 0x000080FF #define GRRLIB_PURPLE 0x800080FF #define GRRLIB_TEAL 0x008080FF #define GRRLIB_GRAY 0x808080FF #define GRRLIB_SILVER 0xC0C0C0FF #define GRRLIB_RED 0xFF0000FF #define GRRLIB_LIME 0x00FF00FF #define GRRLIB_YELLOW 0xFFFF00FF #define GRRLIB_BLUE 0x0000FFFF #define GRRLIB_FUCHSIA 0xFF00FFFF #define GRRLIB_AQUA 0x00FFFFFF #define GRRLIB_WHITE 0xFFFFFFFF #define GRRLIB_LBLUE 0x5050FFFF #define _TITLE "pacman" #define _TEXT2 "pulsa + para modo panico" #define _FONT_WIDTH_PAC 66 #define _FONT_HEIGHT_PAC 49 #define _FONT_WIDTH_ARIAL 16 #define _FONT_HEIGHT_ARIAL 23 #define _IZQUIERDA 0 #define _ARRIBA 1 #define _DERECHA 2 #define _ABAJO 3 int velpacman = 4; int velghost = 4; int dirghost[4] = {_DERECHA,_DERECHA,_DERECHA,_DERECHA}; int frameghost = 0; int xghost[4] = {0,64,128,192}; int yghost[4] = {100,100,100,100}; int framepacman = 0; int xpacman = 256; int ypacman = 100; int ciclos = 0; int dirpacman = _DERECHA; GRRLIB_texImg* tex_ghost[4]; GRRLIB_texImg* tex_pacman; bool panicmode = 0; int resx = 640; int resy=520; void gestiona_pacman (){ if (dirpacman == _DERECHA) { xpacman+=velpacman; GRRLIB_DrawTile (xpacman, ypacman, tex_pacman, 0, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _IZQUIERDA) { xpacman-=velpacman; GRRLIB_DrawTile (xpacman+64, ypacman+64, tex_pacman, 180, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _ARRIBA) { ypacman-=velpacman; GRRLIB_DrawTile (xpacman, ypacman+64, tex_pacman, 270, 1, 1, GRRLIB_WHITE, framepacman); } if (dirpacman == _ABAJO) { ypacman+=velpacman; GRRLIB_DrawTile (xpacman+64, ypacman, tex_pacman, 90, 1, 1, GRRLIB_WHITE, framepacman); } if (xpacman > resx) xpacman = 0; if (xpacman < 0) xpacman = resx; if (ypacman < 0) ypacman = resy; if (ypacman > resy) ypacman = 0; } void gestiona_fantasma (int i){ // compara la posición del fantasma con el pacman // y elige la dirección correcta para perseguirlo // o huir en caso de modo pánico int dirx = -1; int diry = -1; if (panicmode){ if ((xghost[i] > 64) && (xghost[i] < xpacman)) dirx = _IZQUIERDA; if ((xghost[i] < resx-64) && (xghost[i] > xpacman)) dirx = _DERECHA; if ((yghost[i] > 64) && (yghost[i] < ypacman)) diry = _ARRIBA; if ((yghost[i] < resy-64) && (yghost[i] > ypacman)) diry = _ABAJO; } else { if (xghost[i] < xpacman) dirx = _DERECHA; if (xghost[i] > xpacman) dirx = _IZQUIERDA; if (yghost[i] < ypacman) diry = _ABAJO; if (yghost[i] > ypacman) diry = _ARRIBA; } // tres posibilidades // 0: no cambia de dirección // 1: cambia la dirección del eje x // 2: cambia la dirección del eje y int newdir = rand() % 3; if (newdir==1) if (dirx >= 0) dirghost[i]= dirx; if (newdir==2) if (diry >= 0) dirghost[i]= diry; // finalmente movemos el fantasma if (dirghost[i] == _DERECHA) xghost[i]+=velghost; if (dirghost[i] == _IZQUIERDA) xghost[i]-=velghost; if (dirghost[i] == _ARRIBA) yghost[i]-=velghost; if (dirghost[i] == _ABAJO) yghost[i]+=velghost; if (!panicmode) GRRLIB_DrawTile (xghost[i], yghost[i], tex_ghost[i], 0, 1, 1, GRRLIB_WHITE, dirghost[i]*3+frameghost); else GRRLIB_DrawTile (xghost[i], yghost[i], tex_ghost[i], 0, 1, 1, GRRLIB_WHITE, 4*3+frameghost); if (xghost[i] > resx) xghost[i] = 0; if (xghost[i] < 0) xghost[i] = resx; if (yghost[i] < 0) yghost[i] = resy; if (yghost[i] > resy) yghost[i] = 0; } int main(int argc, char **argv) { int i; // Inicializar gráficos GRRLIB_Init(); // Inicializar wiimotes WPAD_Init(); WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR); WPAD_SetVRes(WPAD_CHAN_ALL, resx, resy); tex_ghost[0] = GRRLIB_LoadTexture(pinky_png); tex_ghost[1] = GRRLIB_LoadTexture(blinky_png); tex_ghost[2] = GRRLIB_LoadTexture(inky_png); tex_ghost[3] = GRRLIB_LoadTexture(clyde_png); tex_pacman = GRRLIB_LoadTexture(pacman_png); GRRLIB_texImg* tex_pacfont = GRRLIB_LoadTexture(pacfont_36_png); GRRLIB_texImg* tex_arial_black = GRRLIB_LoadTexture(arial_black_12_png); GRRLIB_InitTileSet (tex_pacman, 64, 64, 0); for (i=0; i<4; i++) GRRLIB_InitTileSet (tex_ghost[i], 64, 64, 0); GRRLIB_InitTileSet (tex_pacfont, _FONT_WIDTH_PAC, _FONT_HEIGHT_PAC, 32); GRRLIB_InitTileSet (tex_arial_black, _FONT_WIDTH_ARIAL, _FONT_HEIGHT_ARIAL, 32); bool done = false; while(1){ WPAD_ScanPads(); // Leer mandos GRRLIB_Printf((640-(_FONT_WIDTH_PAC*strlen(_TITLE)))/2, 360, tex_pacfont, GRRLIB_YELLOW, 1, _TITLE); GRRLIB_Printf((640-(_FONT_WIDTH_ARIAL*strlen(_TEXT2)))/2, 440, tex_arial_black, GRRLIB_PURPLE, 1, _TEXT2); // Si pulsamos home salimos del bucle if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break; if (WPAD_ButtonsDown(0) & WPAD_BUTTON_PLUS) panicmode = !panicmode; if (WPAD_ButtonsDown(0) & WPAD_BUTTON_UP) dirpacman = _ARRIBA; if (WPAD_ButtonsDown(0) & WPAD_BUTTON_DOWN) dirpacman = _ABAJO; if (WPAD_ButtonsDown(0) & WPAD_BUTTON_RIGHT) dirpacman = _DERECHA; if (WPAD_ButtonsDown(0) & WPAD_BUTTON_LEFT) dirpacman = _IZQUIERDA; gestiona_pacman (); int i; for (i=0; i<4; i++){ gestiona_fantasma (i); } ciclos++; if (ciclos >= 3) { ciclos = 0; framepacman++; if (framepacman > 4) framepacman = 0; frameghost++; if (frameghost > 2) frameghost = 0; } GRRLIB_Render(); // Refrescamos la pantalla } GRRLIB_FreeTexture(tex_pacman); for (i=0; i<4; i++) GRRLIB_FreeTexture(tex_ghost[i]); GRRLIB_FreeTexture(tex_pacfont); GRRLIB_Exit(); // Liberamos memoria exit(0); // Usar exit para salir del program (no usar return desde el main) }
Comentarios
PASO ATRAS MIRANDO HACIA DELANTE
Buenas
Doy un paso atras para seguir adelante aplicando los cursos de programacion. Ya comente en otra de las entrega del curso, me habia saltado algunos tutos para ir directamente a lo que estaba pensando en hacer, y ahora vuelvo a este para aprender a aplicar mas cosas, que me hacen falta en este momento... No me enrollo mas..
Veo que el uso de tile y tilesets es aplicar texturas en 2D, cogiendo parte de un archivo PNG.
Por otro lado, en el juego que estoy creando ya he desarrollado (casi todo) en 3D. Un gran entendido de esto, Alejandro, me ha comentado que puedo mejorar el programa, disminueyendo el numero de texturas a cargar, utilizando tiles.
En el presente tutto, esta todo claro, incluso recomendaciones para crear los tileset, y de hay mi pregunta...
¿ Es posible cargar en dibujos 3D los tiles?¿Se usaria la carga en 3D usando la carga parcial de una textura?¿O es necesario cambiar los comandos por los de 2D?...
Por otro lado, tambien Alejandro, me ha recomendado usar fuentes Bitmap...¿ funcionan tambien para 3D?
Se wilco, que nos ilustraras a todos.
Saludos
Curso de GRRLIB
Está increíble la 6ta. parte, Wilco2009 eres un sabio de la programación, yo apenas estoy estudiando el lenguaje C++ y los seudocódigos :(, me imagino que cuesta muchísimo trabajo en el curso de GRRLIB, deberías hacer una aplicación maravillosa que nos sea de mucha ayuda.
Un saludo!!
Miiverse ID: BrianRC07 | Mii y Mario Kart 8: BRKart 07
¿Tenés la Wii U y te gustaría jugar backups de Wii?, entra aquí.
Me alegro de que te
Me alegro de que te guste.
Anímate que cuando menos te des cuenta estarás programando perfectamente en Wii (o quizás en Wii U quien sabe).
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
Estupenda esta 6ª
Estupenda esta 6ª parte!!Felicidades y muchísimas gracias.
Ahora ya se pueden hacer muuuchas cosas interesantes. Animo a todo el mundo a intentar hacer sus propios juegos!
Muchas gracias a ti por
Muchas gracias a ti por seguirlos. A ver si es verdad que la gente se anima.....