lunes, 5 de mayo de 2008

Colisiones

El siguiente paso en la programación de nuestro juego no puede ser otro que el de no salirnos de él. Es decir, nuestro héroe debe andar seguro por las sendas y caminos del mundo que le hemos creado, sin salirse. Este tema es el conocido como colisiones y PALib lo resuelve de un modo muy cómodo.

Pero antes de nada vamos a cambiar el mundo. Quiero decir pongamos otro fondo algo más interesante, uno con un lago y un poco más grande, ¿Qué tal este?
Es algo más grande que el anterior y tiene un par de lagos. ¿Rosas? es para jugar con las transparencias. Crearemos otra imagen igual pero de color azul y así tenemos el agua.
Para integrar este nuevo fondo (lo llamaremos fondo.png) haremos los siguientes pasos:

  1. lo guardamos en la carpeta gfx (dentro de source),
  2. ejecutaremos el 'PAGC Frontend',
  3. cargaremos el ini (load ini), así no perdemos el resto de cosas (a nuestro héroe)
  4. Si hemos borrado el fondo anterior y el nuevo se llama fondo con hacer cargar ini ya se cargará en la pestaña background, por lo que únicamente quedaría hacer 'save and convert'.
Ya está tenemos nuevo fondo. Para cargar el fondo de agua será algo parecido. Yo he credo una imagen del mismo tamaño qe la anterior de color azul (agua.png). Así es como debería quedar la pestaña de backgrounds:
ahora veamos los cambios en el código:

En primer lugar la carga de imágenes. Como ya comentamos en le capítulo de los fondos Palib pone a nuestra disposición cuatro niveles para poder superponer fondos, en nuestro caso actual cargaremos el fondo en el nivel 2 y el agua en el tres (el dos se superpone al tres)


PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_TWO, fondo);
PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_THREE, agua);
PA_InitParallaxX(SCREEN_TOP, 0, 0, 256, 0);
PA_InitParallaxY(SCREEN_TOP, 0, 0, 256, 0);


Otro cambio está a la hora de mostrar a nuestro héroe, deberá hacerlo sobre la capa dos y no sobre la tres.

PA_SetSpritePrio(SCREEN_TOP, HEROE_SPRITE, BACKGROUND_TWO);


Antes del while vamos a colocar la pantalla en su sitio:

PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);

Y por último antes del waitForVBL pondremos estas líneas:

if (estadoAnimacion != ANIMACION_PAUSA) {
PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);//cambio
}


Ya tenemos preparado todo para realizar las colisiones. Como hemos comentado antes PaLib nos permite hacerlo de una forma más o menos sencilla. Debemos crear una imagen con las zonas que queremos que sean de libre acceso y las que no.

PaLib permite a partir de una imagen crear un array de números, para ello divide la imagen en cuadrados de 8x8, a modo de baldosas, y a cada cuadrado distinto le asigna un número. Por ejemplo si el primer cuadrado (la esquina superior izquierda) es rosa, a todo cuadrado de ese color lo asignará un 0.
Pongamos un ejemplo, el mapa para nuestro mundo será este:

Si te fijas bien, en la esquina superior izquierda hay un cuadrado rosa, así me aseguro que el 0 es el espacio rosa. ¿Pero como he hecho este mapa? pues usando el PhotoShop.

En la siguiente imagen se puede ver que he creado una imagen con tres capas. La más profunda tiene nuestro mundo. La del medio es donde he creado la zona rosa (con una transparencia del 30%, para ver como va quedando) y por último una capa con una trama de 8x8 (nuestras baldosas).

Al cargar el fichero, mapa.png, en el PAGC Frontend, nos habrá creado el array que deberemos leer para saber si podemos pisar en esa zona o no. El array es lineal por lo que a partir de las corrdenadas X,Y del muñeco (no de la pantalla) deberemos hallar el cuadrante de la imagen dividida en cuadrados de 8x8 y a continuación calcular el índice de ese cuadrante en nuestro array.



NOTA. No hace falta cargar la imagen mapa en el juego, sólo queremos el array.
Y OTRA COSA. Este es el sistema que he seguido yo, seguro que se puede hacer de otras formas.

¿Parece complicado? Pues es más difícil explicarlo que hacerlo.
La siguiente función es capaz de decirnos, a partir de nuestra posición en pantalla, de que tipo es la baldosa que pisamos.

u8 obtenerBaldosa(s32 x, s32 y) {
return mapa_Map[((y>>3) * 92) + (x>>3)]; //92 es el número de cuadrados de 8x8 (736 pixeles de ancho / 8 =92
}

Finalmente, como nuestro personaje, ocupa más de una baldosa pues hay que llamar a obtenerBaldosa cuatro veces. Aquí os dejo todo el código:


#include <PA9.h>

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

#include "devnintendods.h"

#define BACKGROUND_X 80
#define BACKGROUND_Y 132

#define HEROE_PALLETE 0
#define HEROE_SPRITE 0
#define HEROE_X 120
#define HEROE_Y 88
#define HEROE_VELOCIDAD 5

#define ANIMACION_PAUSA 0
#define ANIMACION_ABAJO 1
#define ANIMACION_ARRIBA 2
#define ANIMACION_IZQUIERDA 3
#define ANIMACION_DERECHA 4

#define ESPACIO_LIBRE 0

u8 obtenerBaldosa(s32 x, s32 y);

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

    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_TWO, fondo);
    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_THREE, agua);

    PA_LoadSpritePal(SCREEN_TOP, HEROE_PALLETE,(void*)heroe_Pal);
    PA_CreateSprite(SCREEN_TOP, HEROE_SPRITE, (void*)heroe_Sprite, OBJ_SIZE_16X32, COLOR_MODE_256, HEROE_PALLETE, HEROE_X, HEROE_Y);
    PA_SetSpritePrio(SCREEN_TOP, HEROE_SPRITE, BACKGROUND_TWO);

    s32 fondoX = BACKGROUND_X;
    s32 fondoY = BACKGROUND_Y;
    s32 estadoAnimacion = ANIMACION_PAUSA;

    PA_InitParallaxX(SCREEN_TOP, 0, 0, 256, 0);
    PA_InitParallaxY(SCREEN_TOP, 0, 0, 256, 0);

    PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
    PA_WaitForVBL();
    //PA_InitText(SCREEN_BOTTOM, BACKGROUND_ZERO); // para debugear!!
    while  (1) {
        if (Pad.Held.Up) {
            if (estadoAnimacion != ANIMACION_ARRIBA) {
                estadoAnimacion = ANIMACION_ARRIBA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 3, 5, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);
            }
        } else if (Pad.Held.Down) {
            if (estadoAnimacion != ANIMACION_ABAJO) {
                estadoAnimacion = ANIMACION_ABAJO;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 0, 2, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);
            }
        } else if (Pad.Held.Left) {
            if (estadoAnimacion != ANIMACION_IZQUIERDA) {
                estadoAnimacion = ANIMACION_IZQUIERDA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 1);
            }
        } else if (Pad.Held.Right) {
            if (estadoAnimacion != ANIMACION_DERECHA) {
                estadoAnimacion = ANIMACION_DERECHA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);
            }
        } else {
            estadoAnimacion = ANIMACION_PAUSA;
            PA_StopSpriteAnim(SCREEN_TOP, HEROE_SPRITE);
        }
        if (estadoAnimacion != ANIMACION_PAUSA) {
            s32 x = fondoX + Pad.Held.Right - Pad.Held.Left;
            s32 y = fondoY + Pad.Held.Down - Pad.Held.Up;
            u8 baldosa = obtenerBaldosa(x + HEROE_X + 8, y + HEROE_Y + 16);
            baldosa |= obtenerBaldosa(x + HEROE_X, y + HEROE_Y + 16);
            baldosa |= obtenerBaldosa(x + HEROE_X + 8, y + HEROE_Y + 8);
            baldosa |= obtenerBaldosa(x + HEROE_X, y + HEROE_Y + 8);
            //PA_OutputText(SCREEN_BOTTOM, 0, 6, "baldosa: %d      ", baldosa); // para debugear!!
            if (baldosa == ESPACIO_LIBRE) {
                fondoX = x;
                fondoY = y;
                PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
            } else {
                estadoAnimacion = ANIMACION_PAUSA;
            }
        }
        PA_WaitForVBL();
    }
    return 0;
}

u8 obtenerBaldosa(s32 x, s32 y) {
    return mapa_Map[((y>>3) * 92) + (x>>3)];
}


En la próxima entrega, veremos como hacer que no se suba a lo árboles.

Saludos

12 comentarios:

David Martínez Martínez dijo...

Al fin me atreví con tu tutorial, quería decirte que es buenísimo. ¡Me encanta lo que estás haciendo! En cuanto termine las lecciones que tienes hechas, intentaré hacer yo algo por mi cuenta a ver que sale. ¡Sigue así!

Anónimo dijo...

tengo una pregunta:
para crear el array se pne en PAGC Frontend pero como se crea el array

Inigo dijo...

El array para las colisiones los crea el PAGC Frontend a partir de la imágen de cuadraditos rosas

Anónimo dijo...

Eso lo se pero que ago con la imajen de cuadritos¿?

Inigo dijo...

La imagen de cuadraditos rosas y blancos se llama mapa.png. Al pasarlo por el 'PAGC Frontend' nos crea el array mapa_Map que contendrá unos y ceros. Donde pone un 0 (ESPACIO_LIBRE) es que hay baldosa, y se puede andar.

u8 obtenerBaldosa(s32 x, s32 y) {
return mapa_Map[((y>>3) * 92) + (x>>3)];
}

La imagen se divide en cuadraditos de 8x8 pixeles. El primero de todos, la esquina superior derecha, es rosa, así que será un 0 y el blanco será un 1.

Saludos

Anónimo dijo...

Pero dentro de 'PAGC Frontend'
donde,quiero decir en textures o bagraunds i de qe forma(8 bits ,
16 bits , easybg...)

Inigo dijo...

He añadido una imagen, en el post, de como debe quedar la pantalla del PAGC Frontend.

Espero que te sirva. Saludos

Anónimo dijo...

He estado pensando sobre el juego que acabastes, y pienso que si pusieras el juego (con todas las carpetas, imajenes ,gfx tipo lo que acen con palibexamples nos
podria ser mas facil para ver donde nos equivocamos.

Anónimo dijo...

parece que las colisiones no me salen,por eso dire tal i como lo he echo por si me podeis alludar:
1-poner las imajenes en el PAGC Frontend i crearlas igual que en la imajen + el sprite.
2-Poner en souroce/gfx todo lo que se ha creado.
3-Poner en souroce/gfx/bin el mapa_Map.
4-Copiar el codigo en el main c
y poner build.
El caso es sigue caminando por todas partes i no se para (lo que seria al topar con objetos,casas o el agua)

Inigo dijo...

He dejado en esta página un zip con todo el proyecto. Se llama PokemonDS.zip.

Espero que con esto se solucione todo. Saludos

Unknown dijo...

Hola esta muy bueno tu post. Pero en este me marca un erros al compilar

'mapa_Map' undeclared (first use in this function)

y si comento la linea(99)

return mapa_Map[((y>>3) * 92) + (x>>3)];

corre pero no se mueve. No se si me podria decir que puedo hacer.

TxoniMoni dijo...

A mi me pasa lo mismo. Creo que las nuevas verisones de PAGfxFrontend.exe funcionan diferente. Yo he tenido que hacer algunos cambios para compilar el programa. Por ejemplo eliminar la línea #include "gfx/all_gfx.c" ya que PAGfxFrontend.exe no genera dicho fechero.
Sustituyendo la línea "return mapa_Map[((y>>3) * 92) + (x>>3)];" por "return 0;" El programa se puede compilar, solo que no se comprueban las colisiones.
¿Alguien sabe como solucionar esto?
Mi correo es "jonlandaburu@hotmail.com"