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