miércoles, 24 de diciembre de 2008

Instalar el kit de desarrollo (II)

Hoy es 24 de Diciembre de 2008, y he tenido que volver a instalar el kit de desarrollo DevkitPro. Por supuesto las versiones han cambiado de cuando presenté este blog por primera vez y existe algún cambio. El problema radica en que PALib no es 100% compatible con el entorno de desarrollo, por lo que debemos hacer los siguientes pasos (siempre que queramos instalar el entorno por primer vez):
  1. Descargaremos el kit de desarrollo DevkitPro Updater y lo instalamos en la carpeta C:\devkitPro,
  2. Borramos la carpeta “devkitARM” de C:\devkitPro y lo reemplazamos por devkitARM r21,
  3. Poner el contenido de la librería libfat en C:\devkitPro\libnds,
  4. Descargar el instalador de PAlib e instalarlo en C:\devkitPro.
Y, a disfrutar programando....

Saludos y Feliz Navidad.

jueves, 13 de noviembre de 2008

El reflejo sobre el lago

Por fin llegamos al reflejo de nuestro personaje en el lago. Lo prometimos en la primera entrega de este blog. Este efecto es una de las cosas que dará calidad a nuestro juego. Parece difícil, pero como todo en esto del software, es muy sencillo, sólo hay que conocer la API de PALib y echarle imaginación.

PALib contiene funciones para que un Sprite se visualice con cierta transparencia, es decir, un reflejo en un lago no va a ser una imagen tan nítida como el original. ¡¡¡Esta función la usaremos!!!

También tenemos una función que posibilita girar un Sprite, (que ya la hemos utilizado), y resulta que el reflejo no es más que nuestro Sprite girado verticalmente. ¡¡¡ Esta también la usaremos!!!

Y Por último, el reflejo se verá sobre el agua, no sobre la hierba. Así que el Sprite andará sobre la capa del agua, por debajo del fondo, y alehop!!!, ya tenemos nuestro reflejo.

¡Parecía imposible!

1. PA_CreateSprite(SCREEN_TOP, HEROE_REFLEJO_SPRITE, (void*)heroe_Sprite, OBJ_SIZE_16X32, COLOR_MODE_256, HEROE_PALLETE, HEROE_X, HEROE_Y + 8);
2. PA_SetSpritePrio(SCREEN_TOP, HEROE_REFLEJO_SPRITE, BACKGROUND_THREE);
3. PA_SetSpriteVflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
4. PA_SetSpriteMode(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
5. PA_EnableSpecialFx(SCREEN_TOP, SFX_ALPHA, 0, SFX_BG0 | SFX_BG1 | SFX_BG2 | SFX_BG3 | SFX_BD);
6. PA_SetSFXAlpha(SCREEN_TOP, 12, 15);

Vamos a explicar paso a paso:

En 1 cargamos a nuestro héroe pero con el identificador HEROE_REFLEJO_SPRITE.
En 2 mandamos el reflejo a la capa sobre el agua.
En 3 giramos verticalmente el Sprite.
En 4 - 6 habilitamos la transparencia.
Y luego no olvidarnos de que al mover a nuestro héroe movamos también a su reflejo.



Aquí os dejo el código (Como siempre, esta es la forma que me he inventado. No creo que exista una función en PALib que cree una sombra de forma automática, pero...):

#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_REFLEJO_SPRITE 1
#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_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, sobre_fondo);

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

PA_CreateSprite(SCREEN_TOP, HEROE_REFLEJO_SPRITE, (void*)heroe_Sprite, OBJ_SIZE_16X32, COLOR_MODE_256, HEROE_PALLETE, HEROE_X, HEROE_Y + 8);
PA_SetSpritePrio(SCREEN_TOP, HEROE_REFLEJO_SPRITE, BACKGROUND_THREE);
PA_SetSpriteVflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
PA_SetSpriteMode(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
PA_EnableSpecialFx(SCREEN_TOP, SFX_ALPHA, 0, SFX_BG0 | SFX_BG1 | SFX_BG2 | SFX_BG3 | SFX_BD);
PA_SetSFXAlpha(SCREEN_TOP, 12, 15);

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

PA_InitParallaxX(SCREEN_TOP, 0, 256, 256, 0);
PA_InitParallaxY(SCREEN_TOP, 0, 256, 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);

PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 3, 5, HEROE_VELOCIDAD);
PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_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);

PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0, 2, HEROE_VELOCIDAD);
PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_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);

PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 6, 8, HEROE_VELOCIDAD);
PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_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);

PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 6, 8, HEROE_VELOCIDAD);
PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0);
}
} else {
estadoAnimacion = ANIMACION_PAUSA;
PA_StopSpriteAnim(SCREEN_TOP, HEROE_SPRITE);
PA_StopSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_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)];
}


Este mismo efecto se puede hacer sobre otro tipo de suelo y da un aspecto de suelo brillante muy chulo.

En la próxima entrega crearemos algún efecto más para la portada del juego y así vamos dando forma a los últimos detalles de nuestro juego.

Saludos

lunes, 10 de noviembre de 2008

Ultimos efectos gráficos

En estos momentos el juego está ya muy avanzado, únicamente tenemos el pequeño problemilla, de que nuestro héroe se sube a los árboles y a los setos, y eso no puede ser.

Para arreglar esto jugaremos con las capas que nos da PALib. Recordemos que tenemos cuatro posibles fondos y que nuestros Sprites (en este caso nuestro héroe) puede andar por encima o por debajo de ellas.

Lo primero de todo es crear una imagen que sea una copia de la imagen fondo y quitar todo lo que nuestro personaje puede pisar: el camino, las flores, la parte inferior de las casas y de lo árboles. Esta es una imagen de como he ido borrando todos esos elementos de la imagen:


NOTA. Cuando decimos pisar no nos referimos a pisar con los pies, si no todo el Sprite, cabeza incluida.

La imagen resultante será esta, que la denominaremos sobre_fondo.png:

Y para cargar la imagen tendremos que cargarla con el gfx, volver a generar y finalmente añadir una línea y modificar dos en nuestro código:

Añadimos: PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, sobre_fondo);

y modificamos:
PA_InitParallaxX(SCREEN_TOP, 0, 256, 256, 0);
PA_InitParallaxY(SCREEN_TOP, 0, 256, 256, 0);

Y este el el resultado, nuestro héroe quedará oculto bajo los árboles y detrás de los tejados:

Para la próxima entrega el efecto sobre el lago!!!!

Saludos

Enlaces patrocinados:

viernes, 7 de noviembre de 2008

Un pequeño respiro

Muy buenas

El mes de octubre no he podido actualizar el blog, pero prometo seguir con él en cuanto me sea posible. Estoy muy agradecido a las personas que han dejado comentarios y que me animan a seguir con el blog.

Pronto tendremos más!!!

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

miércoles, 16 de abril de 2008

Animar nuestro Sprite

Para animar a nuestro héroe únicamente hace falta la sentencia PA_StartSpriteAnim. En ella definimos el sprite inicio y final de la animación así como la velocidad de esta.

Para controlar la animación haremos que cuando se pulse uno de los cursores el muñeco realice la animación de esa dirección, hasta que se pulse otro cursor, o no se deje de pulsar. A mi se me ha ocurrido hacerlo usando una variable que guarde el estado de la animación, con valores tipo ANIMACION_DERECHA, ANIMACION_IZQUIERDA, ANIMACION_PAUSA, ... La variable la llamaremos estadoAnimacion.
El código a incluir será:

Antes del bucle u32 estadoAnimacion = ANIMACION_PAUSA;

El if a poner en el bucle sería algo así:

        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);
        }

Y con esto ya lo tenemos en movimiento.

El código quedará de la siguiente manera:

#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

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

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

    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_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
    PA_WaitForVBL();
    while  (1) {
        fondoX += Pad.Held.Right - Pad.Held.Left;
        fondoY += Pad.Held.Down - Pad.Held.Up;

        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) {
            PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
        }    
        PA_WaitForVBL();
    }
    return 0;
}


¿Qué nos queda? Lo primero que nuestro héroe no se escape del mundo que le hemos creado. PALib, de nuevo, nos ayudará a lograr el que será nuestro próximo objetivo, 'la colisión'.

Un saludo

Enlaces patrocinados
gafas de sol
infografias
Peluches
hipermetropia
despedida de soltera en Cadiz
despedida de soltera en barcelona

Sprites

El siguiente paso en nuestro juego será poner a nuestro héroe a recorrer el mundo que le hemos creado en la anterior entrega.

Como hemos hecho que lo que se mueva sea el fondo, el muñeco estará en el centro de la pantalla.

Pero ¿Qué es un Sprite? Un sprite es un elemento gráfico que se superpone a los fondos, y que gracias a las transparencias, no borra a estos. Vamos a ver nuestro primer sprite:

Muy bien. En este Sprite tenemos a nuestro héroe con las distintas posturas que tendrá en el juego. Guardaremos dicha imagen en C:\devDS\PokemonDS\source\gfx como heroe.png. Para cargar la imagen en el juego, realizaremos los siguientes pasos:
  1. Ejecutar PAGC Frontend (el que está dentro de la carpeta gfx de nuestro source)
  2. Load INI (para cargar el INI donde tenemos el fondo, que si no perdemos el fondo!)
  3. En la pestaña de Sprites cargamos la imagen heroe.png
  4. Save and Convert
Como muchos os habéis imaginado el color rosa es el color transparente, y el espacio que hay entre cada muñeco es porque la DS (o PALib) soporta los siguientes tamaños de Sprites:

8x8 16x8 32x8
8x16 16x16 32x16
8x32 16x32 32x32 64x32
32x64 64x64

En nuestro caso el héroe entraba en una caja de 16x32 y como tenemos 9 posturas, pues la imagen mide 16 x 288.
¿Dónde están las posturas mirando a la izquierda? Pues resulta que podemos rotar al sprite, así que no nos van a hacer falta (porque para este Sprite el ir a la izquierda o a la derecha es idéntico, cosa que no siempre es así).

Pongamos el código necesario. Primero, añadiremos una nueva constante a nuestro fichero devnintendods.h, que quedará así:

#define SCREEN_TOP 1
#define SCREEN_BOTTOM 0

#define BACKGROUND_ZERO 0
#define BACKGROUND_ONE 1
#define BACKGROUND_TWO 2
#define BACKGROUND_THREE 3

#define COLOR_MODE_256 1

y después así es como quedará el main.c:
// Includes
#include <pa9.h> // Include for PA_Lib
#include "gfx/all_gfx.h" #include "gfx/all_gfx.c" #include "devnintendods.h" #define BACKGROUND_X 56 #define BACKGROUND_Y 132 #define HEROE_PALLETE 0 #define HEROE_SPRITE 0 #define HEROE_X 120 #define HEROE_Y 88 int main(int argc, char ** argv) { PA_Init(); PA_InitVBL(); PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_THREE, fondo); PA_InitParallaxX(SCREEN_TOP, 0, 0, 256, 0); PA_InitParallaxY(SCREEN_TOP, 0, 0, 256, 0); 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; while (1) { fondoX += Pad.Held.Right - Pad.Held.Left; fondoY += Pad.Held.Down - Pad.Held.Up; if (Pad.Held.Up) { PA_SetSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 4); PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0); } if (Pad.Held.Down) { PA_SetSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 0); PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0); } if (Pad.Held.Left) { PA_SetSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6); PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 1); } if (Pad.Held.Right) { PA_SetSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6); PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0); } PA_EasyBgScrollXY(SCREEN_TOP, BACKGROUND_THREE, fondoX, fondoY); PA_WaitForVBL(); } return 0; }

El código es muy sencillo: se carga la paleta de colores del sprite, podemos tener paletas compartidas y así ahorramos recursos, y después se crea el sprite. Si os fijáis las variables heroe_Pal y heroe_Sprite las crea el programa PAGC.

Sólo nos falta animar a nuestro héroe. Empezaremos por partes, para esta entrega con girarlo dependiendo de la dirección en que nos desplacemos será suficiente. if (Pad.Held.Left) nos indicará si se ha pulsado el cursor izquierdo, por lo que ponemos el Sprite en el primer frame de la posición izquierda y lo giramos. if (Pad.Held.Right) nos indica que hemos pulsado el cursor derecho y ponemos el frame en la primera posición que refleja el ir a la derecha. Aunque en este caso no haría falta girar el Sprite, como es posible que lo hayamos hecho al ir a la izquierda, tendremos que anular dicho giro.

Vemos una imagen del juego:

Y esto es todo. En la próxima entrega haremos que nuestro héroe mueva las piernas y los brazos.

Un saludo

lunes, 10 de marzo de 2008

Movernos por nuestro juego

Ya tenemos cargado el fondo en nuestro juego, ahora sólo nos queda movernos por él.

Antes de nada vamos a ordenar un poco el código, porque si no la cosa se irá poniendo fea. En primer lugar deberíamos cambiar el nombre al proyecto, o mejor crear un proyecto nuevo, ya que HelloWorld no es lo más apropiado. Quizás un buen nombre sea PokemonDS.

Pues nada manos a la obra:

1. ¿Hemos modificado el PalibTemplate? Con esto me refiero a únicamente crear la carpeta gfx dentro del source y copiar en ella los ficheros que viene en C:\devkitPro\PAlib\Tools\PAGfx.

2. Copiamos PalibTemplate a c:\DevDS y cambiamos el nombre por PokemonDS. Tanto el nombre de la carpeta como dentro del fichero make. Si vamos a usar el Programmer's Notepad deberemos modificar el fichero Template.pnproj por PokemonDS.pnproj y en el interior del fichero cambiar la palabra template por PokemonDS.

3. Vamos a crear una librería, un .h para dejar nuestras constantes y no tener que andar con números raros en las funciones. Esta librería se podría poner en el template para tenerla siempre accesible. La librería la denominaremos 'devnintendods.h'.

En un primer momento la librería tendrá este código:

#define SCREEN_TOP 1
#define SCREEN_BOTTOM 0

#define BACKGROUND_ZERO 0
#define BACKGROUND_ONE 1
#define BACKGROUND_TWO 2
#define BACKGROUND_THREE 3

Con esto tendremos definidas una serie de constante para distinguir las dos ventanas de la DS (SCREEN_TOP -> será la ventana superior) y una serie de constantes para definir los cuatro fondos o backgrounds que puede tener cada pantalla.

¿Para que sirve tener cuatro fondos? Pues jugando con las transparencias se pueden crear efectos muy chulos.

Llegados a este punto sólo nos queda poner el código para mover la pantalla:

1. Para activar el movimiento de un fondo (o de los cuatro a la vez) utilizaremos la rutina PA_InitParallaxX y PA_InitParallaxY, que permite inicializar el movimiento tanto horizontal como vertical y las velocidades de desplazamiento (256 es la normal)

PA_InitParallaxX(SCREEN_TOP, 0, 0, 256, 0); indica que para la pantalla superior de la DS el tercer fondo se moverá a velocidad normal, los otros no se desplazarán.

2. Vamos a movernos utilizando los cursores que hay en la DS. Existe una estructura denominada Pad, que es la que nos permitirá acceder a las pulsaciones de los botones.

El código quedará así:

// Includes
#include <pa9.h> // Include for PA_Lib
#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

#include "devnintendods.h"

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

PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_THREE, fondo);

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

s32 fondoX = 0;
s32 fondoY = 0;
while (1) {
fondoX += Pad.Held.Right - Pad.Held.Left;
fondoY += Pad.Held.Down - Pad.Held.Up;

PA_EasyBgScrollXY(SCREEN_TOP, BACKGROUND_THREE, fondoX, fondoY);
PA_WaitForVBL();
}
return 0;
}

Si ya lo tenéis funcionando, veréis un efecto extraño en el fondo, se solapa en vertical y aparece un espacio negro en horizontal. Esto es por que la imagen no coincide con el tamaño de la pantalla, que es de 256 (0 a 255) por 192 (0 a 191).
Aún así La DS espera fondos de 256x256 (y múltiplos de este tamaño), así que meteremos nuestro fondo en una imagen de 512x512.

Como hemos indicado, el objeto Pad controla todas las teclas de la consola. Estas son todas las teclas:

Pad.Held.Start
Pad.Held.Select
Pad.Held.Right
Pad.Held.Left
Pad.Held.Up
Pad.Held.Down
Pad.Held.A
Pad.Held.B
Pad.Held.X
Pad.Held.Y
Pad.Held.L
Pad.Held.R

Lo de Held es el momento de pulsar el botón (nos olvidamos de ello por ahora).

El aspecto actual de nuestro juego es el siguiente:

Y con esto ya tenemos un poco más de nuestro juego. ahora nos falta poner el muñequito, pero eso será para la próxima entrega.

Saludos

viernes, 7 de marzo de 2008

Cargar fondos

La librería PAlib, nos proporciona unos comandos muy sencillos para realizar una serie de acciones con imágenes. Una de ella es cargar fondos o backgrounds.
Pero antes de escribir más código veamos el que presentamos en el post anterior:

Todo lo que empiece por // es un comentario.

#include <PA9.h> Esto nos permitirá poder usar la librería PAlib.

int main(int argc, char ** argv) { Esta línea define la función main que es la primera función a la que llamará nuestra consola.

PA_Init();
PA_InitVBL(); //Inicializa la librería PAlib.

while (1) {
PA_WaitForVBL();
}

Esto es un bucle. Se ejecutará mientras 1, es decir siempre, por que el 1 es el valor cierto. la sentencia WAIT es la que espera a que se refresque la pantalla y es necesaria para que las aplicaciones no parpadeen.

return 0;
} fin de la aplicación

La imagen que vamos a cargar será esta:
El código necesario para cargar la imágen es este:

PA_EasyBgLoad(0, 3, fondo);

donde 0 es la pantalla de abajo de la DS, 3 es uno de los 4 posibles fondos que puede tener cada pantalla y fondo es el nombre que se da a la imagen desde la aplicación PAGfx.

¿Qué es PAGfx? Es una aplicación que convierte las imágenes de formato gráfico a un formato entendible por PAlib. La aplicación necesita tener .net instalado en tu máquina. PAGfx está en C:\devkitPro\PAlib\Tools\PAGfx.

Yo suelo crear una carpeta denominda gfx dentro de source y copio ahí todos los ficheros de PGAfx (se puede hacer en el template para tenerlo ya preparado). Copiamos la imagen en esa carpeta y ejecutamos el programa PAGC Frontend.exe:


Cargamos la imagen (que guardaremos con el nombre fondo.png) en la pestaña de backgrounds y damos a "Save and Convert".

Incluimos estas dos líneas en nuestro código:

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"
justo debajo del otro include. El código finalmente quedará así:

// Includes
#include        // Include for PA_Lib

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

// Function: main()
int main(int argc, char ** argv) {
    PA_Init();    // Initializes PA_Lib
    PA_InitVBL(); // Initializes a standard VBLPA_InitText(1, 2); //Tell it to use text on screen 1, background number 2 PA_OutputSimpleText(1, 1, 1, "Hello World !");   //Print the text on screen 1, with coordinate 1,1
    PA_EasyBgLoad(0, 3, fondo);

    // Infinite loop to keep the program running
    while (1) {
        PA_WaitForVBL();
    }
    return 0;
} // End of main()
finalmente el programa de HelloWorld quedará así:


En la próxima entrega moveremos la pantalla.

Saludos

Nuestro primer programa, HelloWorld

¿Cómo puedo programar la Nintendo DS? Pues muy fácil siguiendo las instrucciones de este blog. vamos con nuestro primer programa. Trabajaremos en una carpeta denominada c:\devDS, donde iremos dejando nuestros trabajos.

Para empezar a hacer un programa copiaremos la carpeta C:\devkitPro\PAlibTemplate a c:\devDS cambiando el nombre por HelloWorld. Dentro de c:\devDS\HelloWorld cambiaremos el fichero Template.pnproj por HelloWorld.pnproj si queremos trabajar con el Programmers Notepad que viene con el kit. Editaremos el fichero y cambiaremos la palabra Template por HelloWorld.

Por ahora vamos a trabajar con el Programmers Notepad que trae el kit.



Con las teclas Alt+1 podemos compilar la aplicación. Pero claro está, esto es el esqueleto por lo que no hace nada. Modificaremos un poco el proyecto para crear nuestro Hola Mundo.

// Includes
#include <pa9.h> // Include for PA_Lib

// Function: main()
int main(int argc, char ** argv) {
PA_Init(); // Initializes PA_Lib
PA_InitVBL(); // Initializes a standard VBL

PA_InitText(1, 2); //Tell it to use text on screen 1, background number 2
PA_OutputSimpleText(1, 1, 1, "Hello World !"); //Print the text on screen 1, with coordinate 1,1

// Infinite loop to keep the program running
while (1) {
PA_WaitForVBL();
}

return 0;
} // End of main()


Lo compilamos y lanzamos el emulador, cargamos el fichero helloworld.ds.gba y ya tenemos nuestro programa.

En la próxima entrega veremos el código que hemos escrito y empezaremos a cargar imágenes para nuestro juego Pokemon.

Saludos

Instalar el kit de desarrollo

Hoy empezaremos por instalar el kit de desarrollo para nintendo DS.

Cambios a día 24/12/2008.

Lo bajaremos de la web de devkitpro.org. Este kit permite programar utilizando el compilador GC y se instala junto con las librerías necesarias para programar las diferentes consolas. Para la NintendoDS están presentes las librerías ARM, para la Wii y la GameCube las librerías PPC, y para la PSP las librerías PSP.

Guardamos el instalador en nuestro equipo, el programa es devkitProUpdater-1.4.5.exe. Este programa es mejor instalarlo en una carpeta con nombre corto y sin espacios en blanco, por lo que lo guardaremos en c:\devkitpro. Al ejecutar el instalador nos preguntará si queremos mantener los ficheros bajados (para posteriores instalaciones sin tener que acceder a Internet). Tenemos la opción de bajar e instalar más adelante o bajar e instalar de una sola vez.

Para programar la Nintendo DS únicamente es necesario bajar el kit de ARM. Nosotros hemos bajado el de ARM y el de PPC para poder trabajar, en un futuro, con la Wii.


La carpeta devkitpro debería quedar más o menos así:
Instalaremos igualmente la librería PAlib, que nos ayudará a realizar nuestro trabajo. Yo he descargado la última versión estable 'PAlib 070323 Installer' de palib.info.

Descargaremos la librería en la misma carpeta que el kit de desarrollo, en nuestro caso en c:\devkitpro. Ahora la carpeta debe contener todos estos ficheros:

Llegados a este punto sólo nos falta un paso. La librería PAlib no es compatible con devkitARM 2.1 que es la que se baja por defecto con el kit. Pues nada descargamos la versión 2.0 y la instalamos en la carpeta c:\devkitpro, no sin antes renombrar la carpeta C:\devkitPro\devkitARM a C:\devkitPro\devkitARM_21.

Ya tenemos todo. En palibexamples podremos compilar algún proyecto, con los ficheros build.bat.

Muy bien, si hemos llegado a este punto tenemos todo preparado para programar, ¿pero para ejecutar? Para poder ejecutar en nuestro ordenador los programas que hagamos debemos descargar algún emulador. Para mi el mejor es No%GBA. Lo descargamos y lo guardamos dentro de C:\devkitPro\No$gba.

En el próximo post empezaremos a programar.

Saludos

Bienvenido al blog

Desde este blog tenemos la intención de mostrar como programar para la consola Nintendo DS, es decir lo que en la web se conoce como hacer Homebrew para DS. Pronto empezaremos con su hermana mayor la Wii.

Intentaremos explicar todo de tal forma que no se necesiten grandes conocimientos de programación, con un poco de práctica aprenderemos a hacer unos juegos con una presencia totalmente profesional. Utilizaremos el lenguaje de programación C++.

Empezaremos por instalar los programas que nos ayuden a configurar nuestro equipo para poder hacer nuestros primeros programas. Si eres usuario de Windows, son únicamente unos pocos clicks del ratón.

El juego que nos proponemos programar será uno tipo Pokemon. Te mostramos una pantalla de como quedará nuestro juego:



Creo que tiene buen aspecto. Si quieres aprender a programar para la Nintendo DS este es tu blog.

Saludos

Enlaces patrocinados: