Archivo por días: 11 febrero, 2021

Pintando en el Spectrum (13)

En la entrada anterior se me olvidó comentar un detalle extra, que es la tabla de sprites. Esta tabla contiene la información de cualquier sprite que no sea «de fondo»; esto es: todo sprite que vaya a ser animado, o que pueda aparecer o desaparecer, o moverse de sitio, y que, por tanto, no esté definido en el mapa sino que deba pintarse aparte. Estos sprites, al no ser parte del decorado en sí, no pueden almacenarse en la tabla del mapa que vimos antes (la que define las paredes, suelo y objetos fijos), sino que deben definirse aparte.

El primer ejemplo de este tipo de objetos son los propios sprites de los personajes: desde el momento en que éstos se pueden mover de un lado para otro es necesario que no formen parte del mapa. Otro ejemplo son todos los objetos que el protagonista puede coger o dejar, pues éstos aparecerán o desaparecerán del mapa, y también pueden cambiar de lugar en función de donde los deje el jugador. Por último, cualquier objeto animado, como por ejemplo los personajes, o una olla al fuego que hace chup, chup

La tabla de sprites tiene el siguiente formato:

    DEFB 5 ; coordenada Z
           ; si 255-> esta entrada está vacía
           ; si 254-> fin de la tabla
    DEFB 40 ; coordenada X en el mapa
    DEFB 32 ; coordenada Y en el mapa
    DEFB 0x58 ; color de sustitución
    DEFW sprite_base_right ; puntero al sprite-en-sí

En primer lugar tenemos la coordenada Z o de altura. Este byte tiene dos valores especiales: si vale 254 nos indica que es el final de la tabla y, por tanto, que no hay más sprites. Si vale 255 significa que esta entrada nos la debemos saltar porque está vacía. Esto es útil para objetos que pueden aparecer y desaparecer del mapa: cuando no está visible ponemos esta entrada a 255, y no se pintará; cuando vuelva a ser visible, la ponemos al valor de Z correcto.

Justo después están las coordenadas X e Y. La coordenada Y es fundamental a la hora de pintar el mapa. En efecto, si recordamos, cuando pintábamos el mapa lo hacíamos desde las coordenadas Y más bajas (que quedan «arriba» de la pantalla) hacia las más altas. Pues bien: cada vez que se pinta una fila completa del mapa, se recorre la tabla de sprites en busca de todos aquellos cuya coordenada Y coincida con la fila que acabamos de pintar. Si encontramos un sprite que cumpla esta condición, calculamos la coordenada Y de pantalla restando de su coordenada Y la coordenada Z (de manera que cuanto más «alto» esté en Z, más arriba aparecerá en pantalla, pero tapando todos los elementos del mapa que se encuentran antes que la Y del sprite). Es justo esto lo que nos permite simular el efecto 3D, en el que los objetos pueden moverse en los tres ejes, y ocluyendo (o tapando) todo lo que se encuentra detras.

El siguiente byte es el color de sustitución: en mi juego quiero que haya varios personajes, pero todos van a estar hechos con el mismo sprite, así que para distinguirlos cada uno tendrá un color diferente. La manera más sencilla de implementar esto obligaría a tener un conjunto de atributos de color diferente para cada sprite, lo que supondría un consumo excesivo de memoria. Así que decidí que los sprites que tienen máscara de transparencia (lo que incluye al sprite de los personajes) pueden marcar bloques de atributos para que sean sustituidos por este color. Para ello sólo tienen que activar el bit de FLASH y el de BRIGHT a la vez en un atributo concreto, y ese será sustituido por este valor. En cambio, si sólo marca el bit de FLASH, entonces simplemente no se escribirá ese atributo de color concreto, con lo que el color que se utilizará será el que ya había debajo. Esto permite conseguir también «transparencia» con los colores.

Así, en el caso del sprite del personaje, todas las zonas del traje tienen los bits de FLASH y de BRIGHT activos, lo que significa que el color que definí en el editor no se utilizará para esas zonas, sino éste que he definido aquí. Eso es lo que permite tener varios personajes, cada uno de un color diferente, sin consumir memoria extra para meter cada uno.

Por último, tenemos un puntero a los datos del sprite en sí. Esto se hace así para poder cambiar el puntero y, de esta manera, poder animar el sprite si se desea. Por ejemplo, si tenemos dos sprites de una olla que forman una animación, lo que haremos será ir cambiando la dirección del puntero de datos en la entrada de la tabla de sprites de manera que vaya alternando a cual de los dos apunta.