Programación #4: Intercalando datos de sprites en z88dk+splib2

Sigo repasando temas que no quedan realmente claros en la documentación y con los que me tuve que enfrentar en tiempos a la hora de programar mis juegos. Hoy le toca el turno al intercalado de sprites en memoria. Si hemos usado alguna vez la splib2, sabremos que las columnas de los sprites se almacenan en memoria intercalando un byte de gráfico, uno de máscara. La cosa es que si vamos a usar los sprites con OR o XOR no necesitamos la máscara y estamos tirando un byte de memoria por cada byte de gráfico, lo cual no mola en absoluto teniendo tan poca memoria disponible.

sb-sprite2.jpg

El “truco” que me comentó Alvin Albrecht se basaba en intercalar las definiciones de sprites de forma que se ocupen los valores que suelen emplearse para almacenar una máscara guardando los datos gráficos de otra columna de otro sprite. El tema es realmente sencillo y trivial, pero antes hay que entender un poco cómo funciona el tema. Espero que este artículo os ayude a entenderlo al 100% de forma que podáis hacer uso de la técnica cuando no necesitéis máscaras.

Recordemos cuál es el formato normal de una definición de los gráficos de un sprite con máscara y de cómo se llama a la biblioteca splib2 para asignar esos datos a un sprite. Los sprites se definen por columnas. Por ejemplo, para crear un sprite de 16×16 píxels necesitamos 3 columnas de 24 filas (por el tema de las rotaciones tenemos que dejar una tira de bloques extra en vertical y horizontal). Por ejemplo, algo asín:

#asm
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

._uwol_r_1a
    defb @00001111, @11110000
    defb @00001000, @11110000
    defb @00010111, @11100000
    defb @00101110, @11000000
    defb @00101111, @11000000
    defb @00101111, @11000000
    defb @01001110, @10000000
    defb @10110110, @00000000

    defb @10110001, @00000000
    defb @10101110, @00000000
    defb @01001101, @10000000
    defb @00110000, @11000000
    defb @00001011, @11110000
    defb @00010100, @11100000
    defb @00010010, @11100000
    defb @00001001, @11110000

    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

._uwol_r_1b
    defb @11000000, @00111111
    defb @00100000, @00011111
    defb @11010000, @00001111
    defb @00001000, @00000111
    defb @01010100, @00000011
    defb @11111010, @00000001
    defb @11111010, @00000001
    defb @11110100, @00000011

    defb @11001010, @00000001
    defb @00101010, @00000001
    defb @11100100, @00000011
    defb @11011100, @00000011
    defb @00100010, @00000001
    defb @11000010, @00000001
    defb @00000100, @00000011
    defb @00011000, @00000111

    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

._uwol_r_1c
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
#endasm

Si os fijáis, a cada columna la precedemos de una etiqueta (que luego “importamos” al código C con las declaraciones de los punteros extern) que lo que hace es apuntar al inicio de la misma. A la hora de crear nuestro sprite, simplemente le decimos que tiene tres columnas y vamos añadiéndolas indicando siempre la dirección de memoria donde empiezan los datos de la misma:

sp_prueba = sp_CreateSpr (sp_MASK_SPRITE, 3, uwol_r_2a, 1, TRANSPARENT);
sp_AddColSpr (sp_prueba, uwol_r_2b, TRANSPARENT);
sp_AddColSpr (sp_prueba, uwol_r_2c, TRANSPARENT);

Si nosotros empleamos los modos OR o XOR para el sprite, las rutinas que se encargan de dibujarlo se saltarán los datos de la máscara. O sea, ni siquiera miran qué hay ahí. Podemos poner lo que queramos, por ejemplo los datos de otro sprite. Fíjate: las rutinas se posicionan en la dirección de memoria donde empieza la columna (apuntada por el puntero extern que coincide con la posición de la etiqueta en el código asm) leen un byte, se saltan uno, leen otro byte, se saltan otro… Si conseguimos un puntero que apunte a la dirección del anterior más uno (donde estaría el primer byte de su máscara), podemos definir una columna de sprite a partir de ahí, ya que la rutina leerá un byte (gráfico de la segunda columna), se saltará uno (que corresponde a la primera columna), leerá otro (de nuevo de la segunda), etcétera.

¡Joder, qué lío! Vamos a verlo en código: Aquí estamos intercalando las dos primeras columnas de la definición que pusimos antes. Fíjate en las etiquetas:

#asm
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111
    defb @00000000, @11111111

._sp_phantomas_l1_a
    defb @00000000
._sp_phantomas_l2_a
                    defb @00000000
    defb @00001110
                    defb @00000111
    defb @00001111
                    defb @00000111
    defb @00111111
                    defb @00011111
    defb @00000111
                    defb @00000011
    defb @00001111
                    defb @00000111
    defb @00110000
                    defb @00111000
    defb @00111011
                    defb @00111101
    defb @00001100
                    defb @00000100
    defb @00001111
                    defb @00000111
    defb @00000001
                    defb @00000000
    defb @00000011
                    defb @00000001
    defb @00000000
                    defb @00111100
    defb @00001111
                    defb @00111111
    defb @00011111
                    defb @00011111
    defb @00001111
                    defb @00000011
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
    defb @00000000
                    defb @00000000
#endasm

Básicamente es eso: jugar con las etiquetas. Recuerda que una etiqueta asm se importa como un puntero C usando una declaración extern. A partir de ahora, ese puntero contendrá la dirección de memoria donde esté su etiqueta correspondiente, la cual, a su vez, apunta el inicio de una columna del sprite.

Si una columna empieza en la dirección apuntada, por ejemplo, por el puntero uwol_1a (etiqueta ._uwol_1a en el código asm), los datos de su máscara empezarán en uwol_1a + 1 (recuerda: 1 byte de gráfico, 1 de máscara, etc…). Si ponemos una etiqueta ._uwol_1b en la posición de memoria uwol_1a + 1 podemos usarla también como puntero para definir una columna del sprite, y colocar gráficos en la máscara de la primera columna.

No sé si me he explicado. El tema es sencillo de concepto pero difícil de explicar con palabras. Comentadme si os ha quedado claro o no, y revisaré el texto del artículo.

Por cierto, en el código de Phantomas Saga: Infinity podéis ver esta técnica en uso. El código fuente está incluido en el ZIP del juego.

2 Responses to Programación #4: Intercalando datos de sprites en z88dk+splib2

  1. sromero dice:

    Excelente artículo, na_th_an🙂

  2. anjuel dice:

    Estas son las cosas que yo considero “magia”. La forma de poner esto entrelazado me parece un lío de cojones. No veas tu cuando intenté dibujar en un papel los sprites de phantomas!!!😆

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: