viernes, 6 de febrero de 2009

Controlar las fichas

El siguiente paso es crear las fichas y que empiecen a caer por el tablero. Las funciones ya las presentamos en los primeros post de este tutorial así que aquí se presentan todas las de las fichas.

Hemos introducido el control el teclado, donde se puede ver la diferencia entre pulsar una tecla y tenerla pulsada.

Usaremos Pad.Newpress.Right/Left para controlar que pulsen, y suelten, la tecla de ir a la derecha. Esto es para controlar el movimiento horizontal de la ficha de un modo más preciso. Sin embargo, la tecla de bajar se quiere que sea algo rápido, por lo que usaremos Pad.Held.Down (estar pulsado).

Por último, merece la pena ver la función de existe_colision:

01.u8 existe_colision(int x, int y) {
02.    u8 i, j;

03.    for(i = 0; i < FICHA_ANCHO; i++) 
04.        for(j = 0; j < FICHA_ALTO; j++)
05.            if (ficha.ficha[i][j] != CASILLA_VACIA) {
06.                if (x + i < 0) return 1;
07.                if (x + i >= TABLERO_ANCHO) return 1;
08.                if (y + j >= TABLERO_ALTO) return 1;
09.                if (tablero[x + i][y + j] != CASILLA_VACIA) return 1;
10.            }
11.    return 0; //false
12}


Se utiliza el tipo 'int' porque x puede ser negativo.
En la línea 5 se puede ver que únicamente se mirará si alguna casilla de la ficha produce colisión si no está vacía.
Las líneas 6, 7 y 8 miran los bordes del tablero y por fin la línea 9 es la que mira que si en el tablero hay ocupada una casilla se produzca colición.

Otro tema es el de la variable ficha_timer y la constante VELOCIDAD_MINIMA. Se utilizan para que las fichas caigan a una velocidad controlada.

El código:

#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;

u8 ficha_timer = 0;

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

void dibujar_ficha();

u8 existe_colision(int x, int y);
u8 obtener_ficha();

void crear_ficha_t(u8 color);
void crear_ficha_l(u8 color);
void crear_ficha_o(u8 color);
void crear_ficha_z(u8 color);
void crear_ficha_i(u8 color);
void crear_ficha_s(u8 color);
void crear_ficha_j(u8 color);
void girar_ficha_derecha();
void girar_ficha_izquierda();

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();
    obtener_ficha();

    while  (1) {
        if (Pad.Newpress.Left) {
            if (!existe_colision(ficha.x - 1, ficha.y)) ficha.x--;
        } else if (Pad.Newpress.Right) {
            if (!existe_colision(ficha.x + 1, ficha.y)) ficha.x++;
        } else if (Pad.Held.Down) {
            if (!existe_colision(ficha.x, ficha.y + 1)) ficha.y++;
        } else if (Pad.Newpress.Y) {//girar izquierda
            girar_ficha_izquierda();
            if (existe_colision(ficha.x, ficha.y)) girar_ficha_derecha();
        } else if (Pad.Newpress.A) {//girar derecha
            girar_ficha_derecha();
            if (existe_colision(ficha.x, ficha.y)) girar_ficha_izquierda();
        } else {
            ficha_timer++;
            if (ficha_timer > VELOCIDAD_MINIMA) {
                ficha_timer = 0;
                if (!existe_colision(ficha.x, ficha.y + 1)) {
                    ficha.y++;    
                } else {
                    obtener_ficha();
                }
            }
        }
        dibujar_tablero();
        dibujar_ficha();
        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);
}

void dibujar_ficha() {
    u8 i, j;

    for(i = 0; i < FICHA_ANCHO; i++) 
        for(j = 0; j < FICHA_ALTO; j++)
            if (ficha.ficha[i][j] != CASILLA_VACIA) dibujar_casilla(ficha.x + i, ficha.y + j, ficha.ficha[i][j]);
}

u8 existe_colision(int x, int y) {
    u8 i, j;

    for(i = 0; i < FICHA_ANCHO; i++) 
        for(j = 0; j < FICHA_ALTO; j++)
            if (ficha.ficha[i][j] != CASILLA_VACIA) {
                if (x + i < 0) return 1;
                if (x + i >= TABLERO_ANCHO) return 1;
                if (y + j >= TABLERO_ALTO) return 1;
                if (tablero[x + i][y + j] != CASILLA_VACIA) return 1;
            }
    return 0; //false
}

u8 obtener_ficha() {
    u8 tipo = PA_RandMax(6); //0..6
    u8 color = PA_RandMinMax(2, 7); //2..7
    ficha.y = 0;
    ficha.x = TABLERO_ANCHO / 2;
    if (tipo == 0) crear_ficha_t(color);
    else if (tipo == 1) crear_ficha_l(color);
    else if (tipo == 2) crear_ficha_o(color);
    else if (tipo == 3) crear_ficha_z(color);
    else if (tipo == 4) crear_ficha_s(color);
    else if (tipo == 5) crear_ficha_j(color);
    else crear_ficha_i(color);
    return existe_colision(ficha.x, ficha.y);
}

void crear_ficha_t(u8 color) {
    ficha.radio = 3;
    ficha.ficha[0][0] = color;
    ficha.ficha[1][0] = color;
    ficha.ficha[2][0] = color;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = CASILLA_VACIA;
    ficha.ficha[1][1] = color;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = CASILLA_VACIA;
    ficha.ficha[1][2] = CASILLA_VACIA;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_l(u8 color) {
    ficha.radio = 3;
    ficha.ficha[0][0] = color;
    ficha.ficha[1][0] = CASILLA_VACIA;
    ficha.ficha[2][0] = CASILLA_VACIA;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = color;
    ficha.ficha[1][1] = CASILLA_VACIA;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = color;
    ficha.ficha[1][2] = color;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_j(u8 color) {
    ficha.radio = 3;
    ficha.ficha[0][0] = CASILLA_VACIA;
    ficha.ficha[1][0] = color;
    ficha.ficha[2][0] = CASILLA_VACIA;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = CASILLA_VACIA;
    ficha.ficha[1][1] = color;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = color;
    ficha.ficha[1][2] = color;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_o(u8 color) {
    ficha.radio = 2;
    ficha.ficha[0][0] = color;
    ficha.ficha[1][0] = color;
    ficha.ficha[2][0] = CASILLA_VACIA;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = color;
    ficha.ficha[1][1] = color;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = CASILLA_VACIA;
    ficha.ficha[1][2] = CASILLA_VACIA;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_z(u8 color) {
    ficha.radio = 3;
    ficha.ficha[0][0] = color;
    ficha.ficha[1][0] = color;
    ficha.ficha[2][0] = CASILLA_VACIA;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = CASILLA_VACIA;
    ficha.ficha[1][1] = color;
    ficha.ficha[2][1] = color;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = CASILLA_VACIA;
    ficha.ficha[1][2] = CASILLA_VACIA;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_s(u8 color) {
    ficha.radio = 3;
    ficha.ficha[0][0] = CASILLA_VACIA;
    ficha.ficha[1][0] = color;
    ficha.ficha[2][0] = color;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = color;
    ficha.ficha[1][1] = color;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = CASILLA_VACIA;
    ficha.ficha[1][2] = CASILLA_VACIA;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = CASILLA_VACIA;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void crear_ficha_i(u8 color) {
    ficha.radio = 4;
    ficha.ficha[0][0] = color;
    ficha.ficha[1][0] = CASILLA_VACIA;
    ficha.ficha[2][0] = CASILLA_VACIA;
    ficha.ficha[3][0] = CASILLA_VACIA;
    ficha.ficha[0][1] = color;
    ficha.ficha[1][1] = CASILLA_VACIA;
    ficha.ficha[2][1] = CASILLA_VACIA;
    ficha.ficha[3][1] = CASILLA_VACIA;
    ficha.ficha[0][2] = color;
    ficha.ficha[1][2] = CASILLA_VACIA;
    ficha.ficha[2][2] = CASILLA_VACIA;
    ficha.ficha[3][2] = CASILLA_VACIA;
    ficha.ficha[0][3] = color;
    ficha.ficha[1][3] = CASILLA_VACIA;
    ficha.ficha[2][3] = CASILLA_VACIA;
    ficha.ficha[3][3] = CASILLA_VACIA;
}

void girar_ficha_derecha() {
    u8 nueva_ficha[FICHA_ANCHO][FICHA_ALTO];
    u8 i, j;

    for(i = 0; i < FICHA_ANCHO; i++) 
        for(j = 0; j < FICHA_ALTO; j++)
            nueva_ficha[i][j] = ficha.ficha[i][j];
    if (ficha.radio == 3) {
        ficha.ficha[0][0] = nueva_ficha[0][2];
        ficha.ficha[1][0] = nueva_ficha[0][1];
        ficha.ficha[2][0] = nueva_ficha[0][0];
        ficha.ficha[0][1] = nueva_ficha[1][2];
        //ficha.ficha[1][1] = nueva_ficha[1][1];
        ficha.ficha[2][1] = nueva_ficha[1][0];
        ficha.ficha[0][2] = nueva_ficha[2][2];
        ficha.ficha[1][2] = nueva_ficha[2][1];
        ficha.ficha[2][2] = nueva_ficha[2][0];
    } else if (ficha.radio == 4) {
        ficha.ficha[0][0] = nueva_ficha[0][3];
        ficha.ficha[1][0] = nueva_ficha[0][2];
        ficha.ficha[2][0] = nueva_ficha[0][1];
        ficha.ficha[3][0] = nueva_ficha[0][0];
        ficha.ficha[0][1] = nueva_ficha[1][3];
        ficha.ficha[1][1] = nueva_ficha[1][2];
        ficha.ficha[2][1] = nueva_ficha[1][1];
        ficha.ficha[3][1] = nueva_ficha[1][0];
        ficha.ficha[0][2] = nueva_ficha[2][3];
        ficha.ficha[1][2] = nueva_ficha[2][2];
        ficha.ficha[2][2] = nueva_ficha[2][1];
        ficha.ficha[3][2] = nueva_ficha[2][0];
        ficha.ficha[0][3] = nueva_ficha[3][3];
        ficha.ficha[1][3] = nueva_ficha[3][2];
        ficha.ficha[2][3] = nueva_ficha[3][1];
        ficha.ficha[3][3] = nueva_ficha[3][0];
    }
}

void girar_ficha_izquierda() {
    u8 nueva_ficha[FICHA_ANCHO][FICHA_ALTO];
    u8 i, j;

    for(i = 0; i < FICHA_ANCHO; i++) 
        for(j = 0; j < FICHA_ALTO; j++)
            nueva_ficha[i][j] = ficha.ficha[i][j];
    if (ficha.radio == 3) {
        ficha.ficha[0][0] = nueva_ficha[2][0];
        ficha.ficha[1][0] = nueva_ficha[2][1];
        ficha.ficha[2][0] = nueva_ficha[2][2];
        ficha.ficha[0][1] = nueva_ficha[1][0];
        //ficha.ficha[1][1] = nueva_ficha[1][1];
        ficha.ficha[2][1] = nueva_ficha[1][2];
        ficha.ficha[0][2] = nueva_ficha[0][0];
        ficha.ficha[1][2] = nueva_ficha[0][1];
        ficha.ficha[2][2] = nueva_ficha[0][2];
    } else if (ficha.radio == 4) {
        ficha.ficha[0][0] = nueva_ficha[3][0];
        ficha.ficha[1][0] = nueva_ficha[3][1];
        ficha.ficha[2][0] = nueva_ficha[3][2];
        ficha.ficha[3][0] = nueva_ficha[3][3];
        ficha.ficha[0][1] = nueva_ficha[2][0];
        ficha.ficha[1][1] = nueva_ficha[2][1];
        ficha.ficha[2][1] = nueva_ficha[2][2];
        ficha.ficha[3][1] = nueva_ficha[2][3];
        ficha.ficha[0][2] = nueva_ficha[2][0];
        ficha.ficha[1][2] = nueva_ficha[1][1];
        ficha.ficha[2][2] = nueva_ficha[1][2];
        ficha.ficha[3][2] = nueva_ficha[1][3];
        ficha.ficha[0][3] = nueva_ficha[0][0];
        ficha.ficha[1][3] = nueva_ficha[0][1];
        ficha.ficha[2][3] = nueva_ficha[0][2];
        ficha.ficha[3][3] = nueva_ficha[0][3];
    }
}


En el próximo post hablaremos de como manejar el tablero.

Saludos

P.D.: Supongo que os estáis dando cuenta que el código se está haciendo muy grande. En el próximo post publicaremos un zip con todo el proyecto.

Enlaces patrocinados:

5 comentarios:

David Martínez Martínez dijo...

¡Te está quedando muy pero que muy bien el tetris! Aunque si te digo la verdad, el tetris ya lo superé cuando estudiaba java, por lo que este último tutorial no lo estoy siguiendo tan a fondo como el del Pokemon. Eso sí, siempre le echo un vistazo a lo que has hecho, por ver como has abordado el tema. ¡Sigue así, estás haciendo un trabajo tremendo!

Anónimo dijo...

¡dios mio! esto ya me resulta dificil... aunque no por eso voy a dejarlo. gracias por tus tutoriales, iñigo!!

Inigo dijo...

Gracias por los comentarios, me animan a seguir.

Ya estoy preparando el siguiente tutorial, el New Super Mario Bros. Para Marzo empezaremos con él.

¡¡¡espero que salga bien!!!

Unknown dijo...

porque no explicas como crear clases y un poco de programacion orientada a objetos y no tan procedural...

Inigo dijo...

Efectivamente, el siguiente proyecto irá orientado a objetos.

New Super Mario Bros tiene ya los suficientes elementos, y dificultad, como para ser programado con orientación a objetos.

Saludos