Aventuras en Megadrive:
Shadow & Highlight. Castlevania.
Deformando un plano
Este ejemplo está inspirado en la demo que hizo RealBrucest para ilustrar
S&H en el SGDK. Se encuentra en la carpeta sample/hs_effect del SGDK.
Voy a explicar cómo modificar un plano para conseguir efectos que la Mega no puede hacer por si misma. En este caso voy a aprovechar para conseguir S&H de una forma distinta a lo visto hasta ahora.
Voy a realizar un efecto similar al utilizado en esta fase del juego Castlevania.
Es similar al efecto de la entrada anterior, el plano A se usa para mapear prioridades y el plano B se usa para pintar el fondo. Pero en este caso se ven unas diagonales perfectas, algo que con la técnica del post anterior era imposible (sin usar sprites para disimular). ¿Cómo se hace? Modificando el plano A línea por línea.
Línea a línea
Esta es la teoría. Primero dibujamos tal y como hicimos en la entrada anterior.
En el plano A el mapa de prioridades y en el plano B el fondo normal.
A continuación calcularemos un offset o valor de desplazamiento, uno por línea. Según el efecto que queramos mostrar, variará la forma de calcularlo. La idea es tener un vector donde ir guardando ese valor, que será diferente por cada línea. A la resolución habitual, 320x224px, tenemos 224 líneas.
Finalmente, utilizaremos la función VDP_setHorizontalScrollLine() para desplazar cada una de esas líneas ese offset previamente calculado, para uno de los planos.
Vamos a ver un poco de código:
... //Contador u16 i; //Crea dos vectores ("Line scroll buffers") y una variable fix16 line_scroll_data[NUM_LINES]; // Current line scroll values fix16 line_speed_data[NUM_LINES]; // Line scroll speeds s16 aux[NUM_LINES]; // Needed for VDP_setHorizontalScrollLine // Inicializa los vectores "scroll buffers" InitializeScrollTable(); InitializeSpeedTable(); // Screen setting VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); for(i = 0; i < NUM_LINES; i++) { // Sum the speed value line_scroll_data[i] = fix16Add(line_scroll_data[i], line_speed_data[i]); // An auxiliar "regular integer" buffer is needed for VDP_setHorizontalScrollLine aux[i] = fix16ToInt(line_scroll_data[i]); }// end for(NUM_LINES) // Este comando le dice cuanto tiene que desplazar el scroll del plano A (PLAN_A), // para cada línea, empezando desde la cero (0) con un offset determinado (aux). // El numero de lineas es (NUM_LINES) en este caso toda la pantalla, y (1) es // cómo hacerlo, en este caso usando la CPU, y por tanto esto se hará durante el // período donde no se dibuja en pantalla VDP_setHorizontalScrollLine(PLAN_A, 0, aux, NUM_LINES, 1); //al desplazar TODO el plano A, el texto también sale "en diagonal" ...
Creamos tres vectores con tantas posiciones como líneas tiene la pantalla:
- line_scroll_data[]: vector donde se guardan los valores actuales de offset.
- line_speed_data[]: vector de velocidades o aceleraci. Para cada línea se guarda la velocidad a la que se va a mover esa línea. Dependerá del efecto que busquemos.
- aux[]: Recoge los valores de line_scroll_data[] pasando los valores a enteros, el motivo es que VDP_setHorizontalScrollLine() sólo acepta enteros.
Inicializamos estos dos vectores:
- InitializeScrollTable() : Simplemente pone a 0 el vector line_scroll_data[]. Recordemos que en C no se limpia la memoria y por tanto podría contener cualquier cosa al reservar memoria.
- InitializeSpeedTable() : Establece los valores de velocidad.
Tras esto establecemos un scroll horizontal, esta función le dice a la Mega que vamos a mover un plano en la horizontal línea a línea (el plano vertical nos da igual):
VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE)
Ahora con un bucle FOR para cada línea establecemos su offset. Para cada línea, sumamos su valor de offset actual con el valor de velocidad (fijo). Además pasamos el valor a enteros y lo guardamos en aux[].
Terminado el bucle, pintamos el plano A con el offset guardado en aux[]:
VDP_setHorizontalScrollLine(PLAN_A, 0, aux, NUM_LINES, 1);
LA MEGA DE 10 LÍNEAS
Pongamos una hipotética megadrive que tuviese sólo 10 líneas horizontales.
En este caso y siguiendo los pasos mencionados:
Paso 1: Creamos los vectores y los inicializamos.
line_scroll_data[]={0,0,0,0,0,0,0,0,0,0}; line_speed_data[]={0,1,2,3,4,5,6,7,8,9}; aux[] = {basura}
Paso 2: Establece el scroll horizontal, por línea (el vertical nos da igual).
VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE);
Paso 3: Bucle. Tras su ejecución esto son los valores que hay en memoria:
line_scroll_data[]={0,1,2,3,4,5,6,7,8,9}; //<-- esto son fix16 aux[]={0,1,2,3,4,5,6,7,8,9}; //<-- esto son enteros
Es decir, las líneas horizontales se desplazan hacia la derecha de la pantalla, cada una un píxel más a la izquierda que la anterior, es decir, estoy buscando este efecto:
Sólo falta llamar a la función VDP_setHorizontalScrollLine() con los parámetros adecuados para que mueva las líneas.
Por supuesto si queremos que este desplazamiento sea continuo, hemos de incluir el bucle dentro del bucle principal del programa. Eso ya depende de lo que busquemos.
CASTLEVANIA
En mi github tienes un ejemplo que recoge todo lo explicado hasta ahora, tanto para sprites como para fondos. En la parte superior tienes ayuda en pantalla, en la parte inferior algunos comentarios.
Ahora fíjate en como cambia la muerte al cambiar la paleta. Comienzo pintando la muerte con el color 13 de la paleta (que es un color normal). Al cambiar a los colores 14 y 15, se pinta como highlight y como shadow respectivamente.
Al estar el fondo con shadow, la muerte no se ve. He pintado en un color diferente 14 y 15 para que sepas cual se está pintando en cada momento y se vea dónde está el sprite aunque esté fundido con el fondo.
El ejemplo incluye también un ejemplo de mapa de prioridades y desplazamiento del plano A para simular que éste se deforma.
Este es el mapa de prioridad, fíjate que he dejado unas marcas para ver como se pinta en pantalla. Puramente didáctico.
Verás estas marcas en la ejecución de la ROM:
¿Qué otros usos podríamos darle? Existen decenas. Por ejemplo: Un fondo en llamas estático, que parece moverse, fondo de agua que simula estar en movimiento, deformar el escenario, etc.
GITHUB
El código de esta lección, y de todas las demás, lo podrás encontrar en mi github: