8BP: El Sprite Flipping y la Sobreescritura de Sprites
Y como no podía ser de otro modo, tenemos una nueva entrega del curso de programación de juegos para Amstrad CPC de @8bitsdepoder. En este capitulo hablaremos del Sprite Flipping y la sobreescritura de sprites.
Pero espera… ¿Es posible que no sepas nada de 8 Bits de Poder? Puedes leer el primer artículo del curso de programación en Amstrad CPC de 8BP.
¿Estas preparado? ¡Vamos a ello!
En muchas ocasiones necesitarás dibujar personajes que caminan en diferentes direcciones, con diferentes imágenes para cada caso. La imagen del sprite en dirección izquierda será la imagen especular de la dirección derecha. Se pueden definir dos imágenes y almacenarlas en memoria, pero desde la V33, hay una forma de evitar el consumo de memoria RAM para estas imágenes. Se trata de las imágenes “flipeadas”.
Una imagen flipeada es la imagen especular de otra imagen que se ha creado e incluido en el fichero de imágenes. Definiendo una imagen de esta manera, se evita tener que almacenarla. Para hacerlo, simplemente debes incluir una lista de imágenes flipeadas dentro del fichero de imágenes (al que normalmente llamo “images_mygame.asm”). Encontrarás al principio del fichero una sección delimitada por las etiquetas “_BEGIN_FLIP_IMAGES” y “_END_FLIP_IMAGES” destinada a este propósito.
;--------------------------------------_BEGIN_FLIP_IMAGES ; aqui pon las imagenes que se definen como otras existentes ; pero flipeadas horizontalmente. JOE_LEFT dw JOE_RIGHT ; joe_left sera la version flipeada de joe_right ; los frames del soldado a la izquierda los defino como flipeados SOLDADO_L0 dw SOLDADO_R0; SOLDADO_L1 dw SOLDADO_R1; SOLDADO_L2 dw SOLDADO_R2; SOLDADO_L1_UP dw SOLDADO_R1_UP SOLDADO_L1_DOWN dw SOLDADO_R1_DOWN _END_FLIP_IMAGES ;-------------------------------------------------------------------
Las imágenes flipeadas las puedes usar igual que si fuesen imágenes normales. Mas adelante encontrarás como crear secuencias de animación, las cuales puedes construir con imágenes flipeadas y no flipeadas. A todos los efectos, es como si una imagen “flipeada” fuese real, aunque se trata de una imagen “virtual”, que no está almacenada y que al imprimirla se calcula como la imagen especular de otra que sí existe. Las imágenes flipeadas se soportan tanto en mode 0 como en mode 1.
¿Qué es el Sprite Flipping?
El inconveniente de las imágenes flipeadas es que su impresión tiene mayor coste, concretamente consume un 1.8 veces el tiempo que consume una impresión normal, lo cual se podría traducir en una menor velocidad de tu juego. Si tu juego es un arcade (un “shoot’em up”) en el que necesitas la máxima velocidad, mi recomendación es no usar imágenes flipeadas masivamente. Sin embargo, en juegos de aventuras, de pasar pantallas, de laberintos, etc, es una excelente opción. De todos modos, prueba en tu arcade a usarlas pues si no hay muchas flipeadas a la vez, la velocidad resultante puede ser muy aceptable.
He hecho el flipping horizontal y no el vertical porque normalmente un personaje que camina a la izquierda es la imagen especular del mismo caminando hacia la derecha, mientras que si sube muestra la espalda y al bajar muestra el pecho y la cara. Por lo tanto, el flipping vertical no es tan útil como el horizontal, y en aras de reducir el tamaño de 8BP, no lo he incluido entre sus capacidades.
💡 IMPORTANTE: el flipping no es aplicable a las imágenes de tipo segmento que se pueden usar en el modo pseudo-3D de 8BP.
Sprites con sobreescritura
Desde la versión v22 de 8BP es posible editar sprites transparentes, es decir, que pueden sobrevolar un fondo y lo restablecen al pasar. Para ello los sprites que disfruten de esta posibilidad deben ser configurados con un “1” en el flag de sobreescritura del byte de estado (bit 6). En el siguiente apartado se detallará debidamente el byte de status. Veamos cómo se edita un sprite con esta capacidad con SPEDIT.
Muchos juegos utilizan una técnica llamada “doble buffer” para poder restablecer el fondo cuando un sprite se mueve por la pantalla. Se basa en tener una copia de la pantalla (o del área de juego) en otra zona de memoria, de modo que, aunque nuestros sprites destruyan el fondo, siempre podemos consultar en dicha área que había debajo y así restablecerlo.
En realidad, ese es el principio básico, pero es algo más complejo. Se imprime en el doble buffer (también llamado “backbuffer”) y cuando ya está todo impreso, se vuelca a la pantalla o bien se hace conmutar la dirección de comienzo de la memoria de vídeo desde la dirección original de pantalla a la nueva, la del doble buffer, con lo cual, la conmutación es instantánea.
Para construir el siguiente fotograma se usa la dirección de pantalla original donde ahora ya no está apuntando la memoria de vídeo. Allí se construye el nuevo fotograma y se vuelve a conmutar, alternativamente, en cada fotograma. Estas técnicas, aunque funcionan muy bien, tienen un par de desventajas para nuestros propósitos: llevan más tiempo de CPU y consumen mucha más memoria (hasta 16KB adicionales), dejándonos muy poca memoria para nuestro programa BASIC.
Si un juego se desarrolla enteramente en ensamblador, esto no es tan grave porque 10KB de ensamblador dan para mucho, pero 10KB de BASIC es poco. Algunos videojuegos reducen el área de juego para no gastar tanta memoria, pero eso los hace algo mas pobres.
La solución adoptada en 8BP está inspirada en el programador Paul Shirley (autor de “misión Genocide”), pero es ligeramente diferente. Contaré directamente la de 8BP:
La idea consiste en que el fondo nunca es destruido por los sprites que pasan por encima, por lo que no es necesario guardarlo. Esta aparente “magia” tiene su lógica: consiste en “esconder” el color de fondo en el color del sprite que se pinta sobre él.
En el AMSTRAD un pixel de mode 0 es representado con 4 bits, por lo que son posibles hasta 16 tintas diferentes de una paleta de 27 colores. Pues bien, si usamos un bit para el color de fondo y 3 para los colores de los sprites, tendremos un total de 2 colores de fondo + 7 colores + 1 color para indicar transparencia = 9 colores en total. Esto nos va a permitir “esconder” el color de fondo en el color del sprite, aunque pagamos el precio de reducir el número de colores de 16 a tan sólo 9. Además, el fondo solo podrá ser de dos colores.
Por favor, acepta las cookies de YouTube para poder ver este video. Aceptando, accederás al contenido de YouTube, un servicio externo y gestionado por terceros.
Leer la privacidad de Youtube.
Aceptando este aviso, tu selección será guardara y la página se refrescará.
Sin embargo, ciertos elementos ornamentales de la pantalla de juego pueden tener más color, pues los sprites no pasarán por encima (como las hojas de los árboles o el tejado del ejemplo siguiente) de modo que podemos conseguir cierta dosis de colorido en nuestro juego.
Para editar este tipo de sprites debemos usar una paleta adecuada, de 9 colores, donde para cada color de sprites se usan dos códigos binarios (los correspondientes al 0 y 1 del bit de fondo). En el SPEDIT hay dos paletas así definidas que puedes usar seleccionando la opción “3” o “4” al escoger la paleta. Se ha construido así:
Como ves, tras el color 0 y 1, todos los colores se repiten dos veces. Tú puedes construir tu propia paleta de este modo. Puedes ayudarte consultando el apéndice de este manual dedicado a la paleta de color.
La técnica se podría resumir diciendo que en realidad el fondo nunca es destruido por los sprites, sino que se “esconde” en los propios sprites que se imprimen sobre el fondo.
Con el editor SPEDIT puedes modificar la paleta a tu gusto sin necesidad de editar manualmente con comandos INK, y permite exportarla para copiarla en nuestros programas BASIC. La exportación se realiza mandando a la impresora los comandos INK que conforman la paleta (la impresora la redirigimos a un fichero desde winape). Disponemos de las teclas z/x para alterar la paleta y de la opción «i» para exportarla al fichero de salida. Este es un ejemplo de lo que exporta (es una paleta sin sobreescritura):
' – -- – BEGIN PALETA – ------ INK 0 , 1 INK 1 , 24 INK 2 , 20 INK 3 , 6 INK 4 , 26 INK 5 , 0 INK 6 , 2 INK 7 , 8 INK 8 , 10 INK 9 , 12 INK 10 , 14 INK 11 , 16 INK 12 , 18 INK 13 , 22 INK 14 , 0 INK 15 , 11 ' – -- – END PALETA – ------
Los sprites que uses para construir los dibujos del fondo sólo podrán tener los colores 0 y 1 pero los sprites que uses para ornamentar, por donde no vayan a pasar los sprites en movimiento pueden usar los 9 colores.
También puedes aumentar el colorido de los decorados con elementos que sean sprites en lugar de fondos, como el caldero verde del ejemplo anterior. De este modo podrás tener resultados muy coloristas.
La tinta 0001 tiene un uso “especial”. Si editas un sprite que no use el flag de sobreescritura, la tinta 1 será simplemente un color. Pero si editas un sprite con flag de sobreescritura activo en su byte de status, al imprimirse se dejarán sin pintar esos píxeles, respetando lo que hubiese debajo. Eso permite que las colisiones entre sprites no sean “rectangulares”, sino que conserven la forma del sprite.
9 Colores en total:
- 2 de fondo.
- 7 para sprites (en realidad 8 pero uno -000- significa transparencia)
- Los elementos ornamentales pueden usar los 9.
A continuación, voy a mostrarte un sprite donde he pintado de tinta 0001 lo que no se va a pintar, es decir, donde ni siquiera se va a restablecer el fondo, ya que con el resto de píxeles a 0000 ya es suficiente para borrar el rastro del sprite mientras se desplaza.
Como podrás imaginar, en el caso del caldero, al ser un sprite que no se desplaza y por lo tanto no se borra a sí mismo, todo su contorno está pintado con la tinta 0001. Ello permite colisiones perfectas, sin formas rectangulares que evidencian que en realidad los sprites son rectángulos. El resultado final es el que se muestra a continuación en una colisión múltiple.
Como habrás podido adivinar, la colisión además de ser perfecta, evidencia que los sprites han sido ordenados según su coordenada Y, de modo que el último en imprimirse es el ubicado en la posición más inferior. Esto se hace con un simple parámetro al imprimir los sprites con el comando |PRINTSPALL, que veremos más adelante.
Las operaciones de impresión con este mecanismo son muy rápidas, sin necesidad de definir lo que se conoce como “mascaras de sprites”. Las máscaras son mapas de bits del tamaño de un sprite que sirven para acelerar las operaciones de impresión. En este caso no son necesarias. La siguiente figura representa una típica máscara asociada a un sprite. Primero se suele hacer la operación AND entre el fondo y la máscara y después se hace el OR con el sprite. En 8BP es más rápido, pues el sprite no toca el bit destinado al fondo, de modo que la operación OR entre el fondo y el sprite respeta el fondo a la vez que pinta el sprite. Si no entiendes esto muy bien, no te preocupes, no es importante entenderlo pues no es necesario en 8BP.
La impresión de sprites con el flag de sobreescritura activo es más costosa que la impresión sin sobreescritura. A pesar de no requerir máscara y ser muy rápida, esta impresión consume aproximadamente 1.6 veces el tiempo que consume la impresión de un sprite sin sobreescritura. Por ese motivo, úsala cuando sea necesario, y no la uses si tu juego no va a tener un dibujo de fondo que los sprites deban respetar. La combinación de sobreescritura y flipping es aun mas costosa (consume 2.2 veces el tiempo de una impresión normal sin sobreescritura ni flipping) de modo que tenlo en cuenta en tus juegos.
La semana que viene hablaremos sobre cómo pintar sprites sobre el fondo y el manejo la tabla de atributos.
Hasta aquí hemos llegado por hoy con el curso de programación en Amstrad CPC de 8 bits de poder Si tienes alguna duda puedes escribir en comentarios y te contestaré lo antes posible.
Todos los recursos, como manuales, ejemplos y juegos compilados de este curso los puedes encontrar en el repositorio Github de 8BP.
Hasta la semana que viene amigos 😄
Interesante método el que se explica 🙂