Lección 05 – Sprites (III) Enemigos

Recordar que todo esto están basados en los ejemplos del SGDK,
carpeta sample > sprite

 

Vamos a partir del ejemplo correspondiente de mi github.

 

Aventuras en Megadrive: Sprites (III)

En la carpeta RES tenemos el archivo enemies.png:

enemies

Tenemos sprites para dos enemigos distintos en el mismo archivo de imagen. Efectivamente, se puede hacer, siempre que los sprites tengan el mismo tamaño (en tiles). En este caso este es el archivo sprite.res:

SPRITE sonic_sprite       “sprite/sonic.png”      6 6 FAST 5
SPRITE enemies_sprite “sprite/enemies.png” 6 4 FAST 5

Tanto la avispa como el cangrejo tienen misma forma rectangular de 6×4 tiles. La avispa tiene 2 frames de animación, el cangrejo tiene 4. Esto es perfectamente posible y no da ningún problema.

Tanto avispa como cangrejo sólo tienen un tipo de animación (recordemos que Sonic tenía hasta 7 en la lección anterior). De nuevo esto no es problema, podríamos tener otra animación para p.e. el cangrejo, no obstante en ese caso es recomendable separar dichos sprites en imágenes distintas, por claridad.

Obviamente para enemigos más complejos, con más animaciones o distintas formas, tendremos que añadir diferentes líneas en el archivo RES.

En este ejemplo, vamos a usar arrays para guardar los datos:

  • enemigo[] : enemigo[0] será la avispa  y enemigo[1] será el cangrejo.
  • enemigoPosx[] y enemigoPosy[] : para guardar la posición de los enemigos.

 

Antes del main:

//Enemigos
Sprite* enemigo[2]; 

//Posición en pantalla de los enemigos
u32 enemigoPosx[2];
u32 enemigoPosy[2];

Ya dentro del main, antes del loop principal:

  //posición
  enemigoPosx[0] = 128;
  enemigoPosy[0] = 164;
  enemigoPosx[1] = 260;
  enemigoPosy[1] = 84;

  //recoge la paleta de los enemigos y la mete en la 4a paleta del sistema
  VDP_setPalette(PAL3,enemies_sprite.palette->data);

  //sprites
  enemigo[0] = SPR_addSprite(&enemies_sprite, enemigoPosx[0], enemigoPosy[0], TILE_ATTR(PAL3, TRUE, FALSE, FALSE));
  enemigo[1] = SPR_addSprite(&enemies_sprite, enemigoPosx[1], enemigoPosy[1], TILE_ATTR(PAL3, TRUE, FALSE, FALSE));

  //asigna la animación correcta (o todos tendrán la primera animación del fichero png)
  SPR_setAnim(enemigo[0], 1); //asigna la animación del cangrejo (la segunda, 1, del png)
  SPR_setAnim(enemigo[1], 0); //asigna la animación de la avispa (la primera, 0, del png) <- no es necesaria

SPR_update(); // <- no es necesaria pero recomendable

Cosas a comentar.

En primer lugar los SPR_addSprite(). Esto asigna al primer y segundo enemigo el sprite y les da posición. Pero por defecto, se asigna la primera animación del archivo PNG, por tanto en este momento ambos enemigos tienen el aspecto de la avispa.

Por eso a continuación usamos los SPR_setAnim() para asignar al cangrejo su sprite. El SPR_setAnim() de la avispa no es necesario (ya lo tiene), pero ponerlo da claridad al código (sobretodo si luego añadimos animaciones al PNG y hacemos cambios).

De la misma forma SPR_updated()  no es necesario, en este caso es buena costumbre ponerlo para mandar la información al VDP para que esté actualizado antes del loop principal. En este ejemplo no se nota su ausencia, es pequeño, en un proyecto serio hay que tenerlo todo a punto antes de entrar en el loop.

 

ENEMIGOS EN MOVIMIENTO

enemies

CANGREJO

El cangrejo se moverá horizontalmente de un lado al otro de la pantalla. Para controlar el sentido del movimiento:

s16 enemigoSentidoMovimiento; //Valdrá 1 a derechas, -1 a izquierdas

Para comprobar CUANDO debe el cangrejo invertir el movimiento, debemos comprobar cuando se sale de la pantalla, ya sea por un lado o por otro.

if(enemigoPosx[0]>=320 || enemigoPosx[0]<=0) enemigoSentidoMovimiento *= -1;

 

AVISPA

La avispa siempre se moverá hacia la izquierda, con un movimiento senoidal.
Al salir por un lado de la pantalla,  el sprite volverá a salir por el lado contrario. Así:

captura34

En el ejemplo pinto la posición ‘y’ del sprite de la avispa para verlo visualmente. De momento estos enemigos son inofensivos, pero todo se andará. Explico como funciona la senoidal.

 

Senos y Cosenos. Tablas pre-calculadas.

Una curva senoidal oscila entre dos valores a lo largo del tiempo, repitiendo este comportamiento constantemente. Se hace equivaler un ángulo (círculo) con un valor en la vertical a lo largo de la horizontal (de la gráfica). El valor máx se denomina amplitud.

Sine_curve_drawing_animation

Calcular Senos y Cosenos sería demasiado lento para la MD. Para evitarlo, en vez de calcular senos y cosenos, consultaremos una tabla con 1024 valores que el SGDK pone a nuestra disposición. Cada valor=ángulo:

  • 0     equivale a 0º.
  • 256 equivale a 90º.
  • 512 equivale a 180º.
  • 768 equivale a 270º.
  • 1024 equivale a 0º (se completa el ciclo).

Valores superiores volverían a dar los valores anteriores (p.e. 1025 sería 1). Ahora bien, ¿cómo se utiliza esta tabla? Primeros hemos de decidir cuanta precisión necesitamos:

sinFix16()/cosFix16(): Utilizan la parte fraccional de num fix16, que son 6 bits, 2^6=64. Significa que la amplitud será 64. Por tanto los valores oscilarán entre +64 y -64.

sinFix32()/cosFix32(): Utilizan la parte fraccional de num fix32, 10 bits, 2^6=1024. Significa que la amplitud será 1024. Por tanto los valores oscilarán entre +1024 y -1024.

Normalmente con sinFix16()/cosFix16() es más que suficiente

Un uso simple sería el siguiente:

POS_Y = CONST + Fn_Senoidal(valor)

  • CONST : es el valor ‘y’ medio sobre el que oscila el sprite.
  • Fn_senoidal: Devuelve un valor que aumentará y disminuirá regularmente. 
  • valor: valor de entrada de la función.

En nuestro ejemplo, la avispa tiene una CONST = 84. Hemos utilizado sinFix16(), que oscila entre +64/-64. Por tanto el valor ‘y’ de la avispa oscilaría entre 20 y 148  ( donde 20 = 84-64  y 148 = 84+64 ).

 

sin_ejemplo_0

 

Sin embargo, el uso más habitual es el siguiente:

POS_Y = CONST + Fn_Senoidal(valor*acelerador) * mod_amplitud

  • acelerador: modifica el valor final de la función senoidal.
  • mod_amplitud: modifica el valor final de la función senoidal.

En nuestro ejemplo:

  • acelerador = 1, mod_amplitud = 2: el valor ‘y’ de la avispa oscilaría entre -44 y 212( donde -44 = 84-64*2  y 148 = 84+64*2 ).

    sin_ejemplo_1

  • acelerador = 10, mod_amplitud = 1: el valor ‘y’ oscila entre 20 y 148, pero lo hace a gran velocidad, se aprecian perfectamente las ondas senoidales.

    sin_ejemplo_2

Así podemos jugar tanto estos valores para crear interesantes efectos.

 

 

GITHUB

El código de esta lección, y de todas las demás, lo podrás encontrar en mi github:

https://github.com/danibusvlc/aventuras-en-megadrive