Jueves, 25 de enero de 2024

Aquí estamos de nuevo para seguir explicando cómo funcionan las rutinas de scroll utilizadas en QBIQS. Hace unos días explicábamos cómo funciona la rutina utilizada en el scroll de los títulos de crédito y hoy nos toca hablar de:

El scroll de las piezas

Como paso previo hay que ver que un QBIQ (que son tanto las partículas que forman las piezas como las que lanzamos desde nuestra nave) tiene una estructura interna. Y eso se puede ver perfectamente en la siguiente imagen que ha sido capturada del editor de niveles del QBIQS:
Los QBIQS de la primera fila son los que tienen los números del 0 al 7 (de izquierda a derecha), mientras que los de la segunda fila son los que tienen los números del 8 al 15. Lo primero que salta a la vista es que ahí falta uno, concretamente el QBIQ número 14 y enseguida entenderemos por qué.

Si analizamos los QBIQS veremos que el aspecto de un QBIQ tiene mucho que ver con su número, ya que está determinado por los 4 bits de la representación binaria del mismo:
Así, el QBIQ 12, cuya representación en binario es 1100 tiene las líneas superior e izquierda, pero no las líneas inferior y derecha. Si nos vamos a su representación gráfica podemos ver que se corresponde al 100%. Ahora ya estamos en condiciones de explicar la anomalía que hay en la representación: el QBIQ 14. Este sería un QBIQ ilegal que no puede aparecer jamás en el juego. Su representación binaria sería 1110, lo que se traduce en un QBIQ con las líneas superior, izquierda y derecha. Vamos, justo al revés que el QBIQ 7 que sí aparece en la imagen (el de más a la derecha de la línea superior). ¿Y por qué no puede aparecer en el juego? Bueno, todas las piezas deben tener un mínimo de dos QBIQS de ancho y es imposible que un QBIQ como el 14 pertenezca a una pieza legal, ya que nunca podría eliminarse. Por eso en los puzzles nunca aparece un QBIQ 14, pero sí que se utiliza, ya que durante el juego representa los QBIQS que van desapareciendo tras haber completado alguna(s) pieza(s).

Una vez vista la anatomía de un QBIQ, lo siguiente que hay que saber es que existe una regla que dice que dos QBIQS consecutivos deben compartir el mismo bit en sus fronteras tanto vertical como horizontalmente. ¿Esto qué significa? Pongamos un ejemplo: si tenemos dos QBIQS horizontales que pertenecen a la misma pieza, el que está a la izquierda deberá tener un 0 en su bit 1 (pues no debería tener línea derecha), mientras que el que está a la derecha tendrá un 0 en su bit 2 (pues no debería tener línea izquierda). Y lo mismo sería en vertical con los bits 0 (del QBIQ superior) y 3 del QBIQ inferior. Si pertenecieran a piezas diferentes, los bits frontera de los lados que se tocan deberán estar a 1. De esta forma se puede saber si dos QBIQS pertenecen a la misma pieza o no.

Pero, ¿qué es de una regla sin excepciones? Esta no es menos y tiene dos: los QBIQS 0 y 15, que actúan justo al revés de esa regla. El QBIQ 0 no tiene fronteras y debería pertenecer a la misma pieza que los cuatro QBIQS que le rodean, pero realmente no pertenece a ninguna pieza, ya que representa un espacio vacío que puede ser atravesado por los QBIQS que lanzamos. Por el contrario el QBIQ 15 (con una frontera teórica en todos sus lados) es un QBIQ que pertenece a la misma pieza que los cuatro QBIQS que le rodean.

Aunque la regla se aplica tanto horizontal como verticalmente, dado que el juego tiene un scroll únicamente vertical, nos interesan solo las combinaciones de QBIQS en vertical. Y ateniéndonos a ella vemos que hay combinaciones que son válidas y otras que no lo son. Por ejemplo, es perfectamente válido que un QBIQ 0 esté encima de un QBIQ 8, 9, 10, 11, 12, 13 o 14 (por aquello de ser usado para los QBIQS que se van eliminando), pero es imposible que esté sobre cualquiera de los otros. Ahí ya nos hemos quitado 9 combinaciones y no son las únicas. En este punto, analicé a mano todas las posibles combinaciones y creé una tabla de 16x16 que me decía si una combinación de dos QBIQS en vertical podía aparecer en el juego o no.

El siguiente paso es definir el aspecto de la zona de scroll del juego. Esta zona se rellena (tanto en modo 1 como 2 jugadores) desde el final hacia el principio del nivel colocando puzzles individuales a una distancia del anterior que es más pequeña a medida que aumentan los niveles. Esto se realiza exactamente igual para ambos modos de juego, siendo la única diferencia el ancho de la zona, que es de 16 QBIQS en modo 1 jugador y de 12 QBIQS para cada jugador en modo 2 jugadores. Como el scroll es (a riesgo de ser pesado) vertical, para un QBIQ determinado nos interesa saber cuáles son los QBIQS que están encima y debajo del mismo. Por esto, la información que se almacena en la zona de scroll es esa precisamente: parejas de QBIQS dispuestos en vertical. Veámoslo con un ejemplo, imaginemos tres QBIQS dispuestos verticalmente en esta forma:
Podemos ver que encima del todo hay un QBIQ 11 (en rojo), bajo él un 12 (en verde) y por último un 7 (en azul), que en hexadecimal serían B, C y 7 respectivamente. Esto se traduce en dos bytes: el byte BC que indica que sobre un QBIQ C hay un QBIQ B (o, análogamente, que bajo un QBIB B hay un QBIQ C) y el byte C7, que indica que sobre un QBIQ 7 hay un QBIB C (o que bajo un QBIQ C hay un QBIQ 7). Estos dos bytes estarán en la misma columna en la zona de scroll (separados 16 o 12 bytes según sea el modo 1 o 2 jugadores). Para modificar un QBIQ en la zona de scroll se tiene que cambiar el nibble superior de un byte y el nibble inferior del byte situado encima de este. En nuestro ejemplo, si quisiéramos modificar el QBIQ C, que está en el centro, por un QBIQ A, esos dos bytes se cambiarían por BA y A7 respectivamente. Esto es lo que hace el juego cada vez que se pega un nuevo QBIQ a los que bajan (modificando también los de los laterales si fuera necesario) y cuando se borran los QBIQS de las piezas eliminadas. Efectivamente, hay redundancia, ya que la información sobre la existencia de un QBIQ está siempre presente en dos bytes. Sin embargo, esta redundancia nos va a ser de gran utilidad a la hora de traducir esta representación interna a su correspondiente representación gráfica.

Cada vez que el scroll avanza 8 pixels (un tile entero) hay un puntero a la zona de scroll que sube 16 (o 12) bytes, mostrando así la siguiente línea del scroll. Pero eso no nos basta, ya que siendo el scroll al pixel, es necesario tener un puntero adicional que nos indique el desplazamiento en vertical intra-tile y que se mueve siempre entre 0 y 7. Con todo esto ya tenemos lo necesario para traducir la zona de scroll a algo visible y que, además, de la total apariencia de que se está realizando un scroll al pixel.

Basándonos en la tabla ya calculada que nos indicaba si una combinación de dos QBIQS en vertical era posible o no, el siguiente paso es analizar para cada pareja posible cuál sería el tile gráfico correspondiente según el desplazamiento intra-tile. Así, cuando avanzamos un pixel hay que desplazar los siete primeros bytes del tile un byte hacia abajo y meter arriba el siguiente byte que define el QBIQ superior. Quizá hayas pensado que son demasiadas combinaciones y que no cabrían en 256 tiles, pero hay que tener en cuenta que muchas combinaciones diferentes nos van a dar exactamente el mismo tile. Por ejemplo si tenemos dos QBIQS 6 uno encima del otro, cualquier desplazamiento entre 0 y 7 va a resultar en el mismo tile gráfico, que sería el formado por dos líneas verticales a izquierda y derecha (pensadlo un poco y lo veréis enseguida). Tras acabar de analizar todas las posibilidades, surgieron un total de 183 tiles gráficos diferentes, que son los que permiten visualizar el scroll al pixel. ¡Premio! La definición de todos ellos cabe perfectamente en VRAM y nos quedan algunos tiles extra para decorar los laterales, los marcadores y las naves.

Ahora tenemos no una, sino 8 tablas (una para cada valor del puntero de desplazamiento intra-tile) que nos indican para cada pareja de QBIQS en vertical, cuál es el tile gráfico correspondiente. Así pues, el núcleo central del scroll son ocho lookup tables de 256 bytes que nos traducen una pareja de QBIQS, tal y como está representada en la zona de scroll, al tile gráfico correspondiente. Variando el puntero de desplazamiento intra-tile se consulta una u otra tabla y se visualiza el scroll suave que presenta el juego. Además, las tablas están alineadas en RAM de forma que el byte bajo de la dirección se corresponde con el índice (de 0 a 255) de cada tabla.

Lo bueno del sistema es que si en lugar de incrementar el puntero intra-tile lo decrementamos, el scroll puede ir al revés. Esto se aprovecha para mostrar el texto de la pantalla de título o los textos que identifican al ganador y perdedor en el modo de 2 jugadores. En la siguiente imagen podemos ver una pequeña animación de los ocho tiles diferentes a los que puede traducirse la pareja de QBIQS C7 del ejemplo anterior según el desplazamiento, tanto en un sentido como en el otro:
Así, la visualización del scroll tiene dos fases, que solo se realizan si hay un cambio en el scroll, bien sea porque avanza o porque hemos modificado el contenido de la zona de scroll. La primera fase consiste en traducir los datos de la zona de scroll en tiles: simplemente hay que recorrer la zona de scroll visible y usar cada byte como índice en la LUT indicada por el puntero de desplazamiento intra-tile. El byte contenido en la tabla será el tile con la representación gráfica que estamos buscando. Una vez ejecutada la primera fase, tendremos una tabla de tiles que serán volcados a VRAM en la segunda fase.

¡Y listos! Así funciona el scroll de las piezas en QBIQS. En este punto tenía tiles suficientes como para introducir decoraciones en los laterales, las cuales eran estáticas en los primeros momentos, como se puede ver en este vídeo de 2007:
Pero alguien me sugirió que las decoraciones se desplazasen, aunque fuera al tile. Tras hacerlo, desplazarlas al pixel fue bastante sencillo... ¡pero eso lo dejamos para el siguiente post!