miércoles, 28 de enero de 2009

Los tiles y cómo dibujar el Tablero

Por fin vamos a ver una nueva técnica que nos ofrece las librerías PALib.

A la hora de programar juegos de tablero, o a la hora de dibujar nuestros mundos, no es necesario cargar unos fondos enormes. PALib ofrece la posibilidad de poder dibujar tiles.

Los tiles son pequeñas casillas que se pueden dibujar en la pantalla tantas veces como se quiera. Son casillas de 8x8 pixeles, por lo que una pantalla de la DS que tiene 256x192 tendrá 32x24 tiles.

La siguiente imágen nos carga los tiles que vamos a usar en esta primera versión del Tetris:



Si os fijáis bien, se han creado una serie de cuadraditos de 8x8 pixeles que serán las posibles casillas a dibujar. Cómo las fichas del Tetris tienen distintos colores pues hemos hecho casillas de diferentes colores.

Ahora veamos el código poco a poco para ver cómo funciona todo:

En primer lugar:

Cargamos la imagen de los tiles en las dos pantallas mediante estas dos funciones de la API de PALib.
PA_DualLoadBgPal(BACKGROUND_TWO, (void *)tiles_Pal);
PA_DualLoadSimpleBg(BACKGROUND_TWO, tiles_Tiles, Blank, BG_256X256, 0, 1);

La primera carga la paleta de la imagen, y la segunda carga, en las dos pantallas, la imagen como si fueran tiles y lo muestra en pantalla.
¿Pero que muestra? Un array de ceros, Blank, de 32x24 en cada una de las pantallas. Como la primera casilla de la imagen (la casilla cero) de tiles es negra, mostrará una pantalla negra.

Para dibujar por la pantalla los distintos tiles se puede utilizar la función PALib:
PA_SetMapTileEx(screen, BACKGROUND_TWO, x1, y1, ficha, 0, 0, 0);
donde x1,y1 serán las coordenadas del array de 32x24 en que está dividida cada pantalla de la DS. Los tres últimos parámetros ya los veremos más adelante.

Para poder ver todo lo que se ha dicho aquí, probar el siguiente código quitando las dos líneas que cargan los fondos: PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top); y PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);.

Dibujar_casilla:

Esta función dibuja cada casilla del tablero y cada bloque de una ficha del tetris. Como 8x8 pixeles es muy pequeño, vamos a dibujar casillas de 16x16, es decir cuatro veces la casilla de 8x8.
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1, ficha, 0, 0, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1 + 1, ficha, 0, 1, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1, ficha, 1, 0, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1 + 1, ficha, 1, 1, 0);

Los parámetros son:
PA_SetMapTileEx(screen, background, x, y, tile, girar_horizontal, girar_vertical, paleta);

Debido a un pequeño problema que ocurre en la pantalla inferior (la posición 0,0 se baja unos 16 pixeles), hay que poner el scroll de esta pantalla a 0 (!?!?!?!?!?) :

PA_EasyBgScrollXY(SCREEN_BOTTOM, BACKGROUND_THREE, 0, 0);

Por último dibujaremos la pantalla de la aplicación. Por ahora he usado estos dos fondos (fondo_top y fondo_bottom)






El código es el siguiente:
#include <PA9.h>

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

#include "devnintendods.h"

#define TABLERO_ANCHO 10
#define TABLERO_ALTO 21

#define FICHA_ANCHO 4
#define FICHA_ALTO 4

#define CASILLA_VACIA 1
#define CASILLA_ROJA 2
#define CASILLA_AZUL 3
#define CASILLA_VERDE 4
#define VELOCIDAD_MINIMA 25
#define BORDE_IZQUIERDO 1

//La fila 0 es la de más arriba y la 16 la de la base. La columna 0 es la de la izquierda.
u8 tablero[TABLERO_ANCHO][TABLERO_ALTO]; 

typedef struct {
    int x, y;
    u8 radio;
    u8 ficha[FICHA_ANCHO][FICHA_ALTO];
} tipo_ficha;

tipo_ficha ficha;

void inicializar_tablero();
void dibujar_tablero();
void dibujar_casilla(int x, int y, u8 ficha);

int main(int argc, char ** argv) {
    PA_Init();
    PA_InitVBL();
    PA_InitText(SCREEN_TOP, BACKGROUND_TWO);

    PA_DualLoadBgPal(BACKGROUND_THREE, (void *)tiles_Pal);
    PA_DualLoadSimpleBg(BACKGROUND_THREE, tiles_Tiles, Blank, BG_256X256, 0, 1);
    PA_EasyBgScrollXY(SCREEN_BOTTOM, BACKGROUND_THREE, 0, 0);

    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top);
    PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);

    inicializar_tablero();
    dibujar_tablero();

    while  (1) {
        PA_WaitForVBL();
    }
}

void inicializar_tablero() {
    u8 i, j;

    for(i = 0; i < TABLERO_ANCHO; i++) 
        for(j = 0; j < TABLERO_ALTO; j++) 
            tablero[i][j] = CASILLA_VACIA;
}

void dibujar_tablero() {
    u8 i, j;

    for(i = 0; i < TABLERO_ANCHO; i++) 
        for(j = 0; j < TABLERO_ALTO; j++)
            dibujar_casilla(i, j, tablero[i][j]);
}

void dibujar_casilla(int x, int y, u8 ficha) {
    u8 x1 = x * 2;
    u8 y1 = y;
    u8 screen = SCREEN_TOP;
    if (y1 >= 12) {
        screen = SCREEN_BOTTOM;
        y1 = y1 - 12;
    }
    y1 = y1 * 2;
    x1 += BORDE_IZQUIERDO;
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1, ficha, 0, 0, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1 + 1, ficha, 0, 1, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1, ficha, 1, 0, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1 + 1, ficha, 1, 1, 0);
}


La programación con 'tiles' nos permitirá crear el juego de plataformas. Si nos da tiempo volveremos sobre el juego de Pokemon para poder hacer mundos más grandes, sin ocupar demasiado espacio en memoria.

Saludos

6 comentarios:

Unknown dijo...

Estaba esperando esto de los tiles, el juego del estilo pokemon yo tb habria usado tiles para hacer el mapa, en especial si fuera mas grande. Pregunta: existe una funcion ( como en J2ME ) para dibujar circulos o cuadrados en este caso, sin el uso de ningun tipo de imagen externa?
Saludos y muy buenas las notas

Inigo dijo...

Si que existen algunas de las funciones.

Puedes mirar aquí. Tienen funciones para hacer rectas, y dibujar pixeles, poco más.

Saludos

David Martínez Martínez dijo...

¡Arg! Nunca me entero de cuando posteas algo, tengo que ponerme tu página en marcadores.

Por cierto, estoy peleándome con dos cosas ahora mismo. Clonar sprites y crear ficheros de texto, modificables con notepad.

La gente del foro palib, serán muy listos, pero son una panda de frikis engreídos, que solo saben mandarte mirar los tutoriales (cosa que hago siempre antes de postear nada) y en todos los posts que he puesto hasta ahora, no me han ayudado nunca en NADA.

Verás, lo de clonar sprites, la verdad no se ni por donde empezar (se que existe la función CloneSprite, pero la verdad, como lo llevo todo cogido con pinzas, no me entero de nada).

Los ficheros de texto, he conseguido hacer cosas, pero cuando intento abrir los archivos que genero, no se pueden leer con el notepad, solamente desde la aplicación que he generado.

Para probar esto, hice un DSfit, (como el WIIfit pero para la DS) muy sencillito, que solo dice, 1, 2, 3, 4, y mientras tu en teoría, bajas y subes un escalón de una escalera. Cuando lo paras, guarda en un fichero de texto el tiempo que lleva, pero no consigo que este fichero pueda leerlo, desde una aplicación externa, como el Notepad.

El tema por lo que quiero hacer esto, es porque quiero hacer un editor de mapas para un juego que estoy haciendo. No quiero cargar ficheros externos para los mapas, porque no quiero que se puedan poner todos los mapas que quieras, quiero copiar los archivos generados con el editor, en el notepad, probarlos yo, para ver si están bien hechos, y luego ponerlos en el código fuente y compilarlos.

Agradecería UNA BARBARIDAD si pudieras hacer un tutorial de alguno de estos temas. Saludos, y perdona por la parrafada.

PD: por cierto, si quieres que te pase el código fuente del DSfit, dímelo, que arregle un poco el código, comente más, y te lo paso sin problemas

Inigo dijo...

Buenas David

Si quieres puedo publicar tu programa en este blog o poner un enlace a donde quieras, o incluso hacer unos post con él.
En cuanto a lo de los ficheros, voy a hacer algunas pruebas y te digo.

Otra cosa, intento 'postear' los viernes pero no siempre es fácil.

Saludos

David Martínez Martínez dijo...

¡Muchas gracias por tu ayuda! Lo del DSfit no lo tengo subido a ningún lado, ni siquiera al correo, así que limpiaré el código, que todavía no lo he hecho, y vemos como lo podemos compartir, para que lo subas al blog, me encantaría ayudar en lo que sea. Y lo de que posteas los viernes, eh... Un consejo, no te lo impongas como una norma. Esto es un hobby, no un trabajo. Si de repente, te tiras 3 días que quieres poner los 3 días algo, pues hazlo. Y si un mes, no tienes ganas de poner nada, pues no lo pongas, nadie se va a enfadar. (supongo...) Gracias por tu tiempo.

David Martínez Martínez dijo...

Respecto al DSfit, he intentado limpiar el código, pero en cuanto toco algo, no se por qué pero el sistema de sonido revienta y se oye solamente estática. En un principio iba a usar EFS para incluir los ficheros dentro de la propia rom, pero tuve problemas con esto y al final no lo hice. Pero, no se como lo hice, que si intento quitar su librería, que en teoría no la uso para nada, revienta el sonido y no carga bien los datos. Intentaré hacer el DSfit de cero, porque esto que hice no está presentable ni mucho menos.