martes, 26 de febrero de 2008

QBIQS (VI) La importancia de un bit

¿Un bit puede ser muy importante?

Pues sí, un bit puede significar la diferencia entre un comportamiento totalmente normal o un programa absolutamente descontrolado. Voy a contaros una historia que me ha sucedido con el QBIQS, espero que la encontréis divertida (aunque hasta que encontré el maldito bit no me reía).


Un programa que se dispara a sí mismo

Cuando he ido a enseñarles el juego a unos compañeros, quienes también me dan consejos y prueban la jugabilidad, han empezado a suceder cosas extrañísimas... la primera de todas que el juego reseteó el MSX. Luego, probándolo más nos dimos cuenta de que la música se desincronizaba, a veces se reseteaba todo, otras veces se quedaba colgado... pero siempre el problema estaba en la rutina de música.

Dicha rutina está basada en el replayer del PT3 originalmente programado por Sergei Bulba para Spectrum y adaptado por Dioniso para MSX. Esta rutina se automodifica para ganar velocidad, con lo que su ejecución debe ser en RAM. Me puse a examinar el código, comparándolo con la salida del ensamblador y me di cuenta de que la rutina de decodificación del PT3 había sido modificada de algún modo por otra parte del programa... concretamente ¡por un impacto de un QBIQ lanzado!

¡Hay que hacer bien las cuentas!

La única posibilidad estaba en el último cambio que había hecho cuando uno de los beta-testers del juego me dijo que los disparos iban algo lentos para su gusto. Había modificado la rutina para, en lugar a 2 pixels por frame, moverlos a 3 pixels por frame. Una vez hecho esto estaba obligado a examinar la rutina de anulación del disparo cuando éste se sale de la pantalla.

Originalmente los disparos se anulaban cuando su coordenada vertical valía -9 y yo estaba convencido de que la coordenada de origen era 179. Así que hice un cálculo rápido 179 - (-9) = 188 que no es divisible por 3, pero 189 sí lo es, así que la nueva coordenada de anulación debe ser -10 (que es $F6 en hexadecimal). Probé el disparo y funcionaba bien, así que anoté el cambio y lo di por bueno.

Sin embargo la coordenada de origen era 159, por lo que 159 - (-10) = 169 no era divisible por 3, de modo que los disparos jamás llegaban a tener la coordenada vertical igual a -10, por lo que no se anulaban. Para acelerar los cálculos existen unas tablas de desplazamiento según la coordenada vertical del disparo, pero esas tablas están pensadas para funcionar hasta el valor -15... valor que sobrepasaban los disparos.

Al aparecer coordenadas verticales para las que no estaba previsto el cálculo, los punteros para la tabla no valían y devolvían unos valores erróneos, situando el punto de impacto en una posición fuera de la zona del scroll... concretamente en el replayer del PT3.

Así que la solución ha sido volver a modificar la coordenada de anulación de los disparos, que nunca debía haber cambiado de -9 (cuyo valor en hexadecimal es $F7).

Esta es la historia de como un simple bit (encima el bit de menos peso de un byte) puede marcar la diferencia entre un comportamiento correcto y el caos.

viernes, 22 de febrero de 2008

QBIQS (V) La luz a través de los QBIQS

¡Por fin!

¡Los QBIQS ya están desapareciendo! La rutina de detección del marco maximal funciona y los QBIQS que bajan ya desaparecen al completar los rectángulos. Obviamente hay que hacer unas cuantas pruebas más, pero es genial poder ver que los QBIQS desaparecen por fin tras un par de años de pensar como detectar los rectángulos rellenos...

En cuanto complete la animación del borrado el motor del juego estará prácticamente terminado... ¡YUJUUUU!

Promete ser un fin de semana interesante en materia de programación :D

Toy contento

jueves, 21 de febrero de 2008

QBIQS (IV) Desesperación...

Pues sí... en menudo embolado me he metido con este juego.

Me he visto obligado a reestructurar todo el bucle de juego para el modo de un jugador porque la rutina de detección de marcos es algo más lenta de lo que esperaba. Eso ha supuesto reescribir un par de rutinas del scroll y crear una nueva rutina de volcado de sprites para este modo, con el curioso efecto de que ahora hay dos rutinas de volcado de sprites en este modo, dependiendo de cuantos sprites queramos volcar.

Al final he logrado distribuir algo más uniformemente el tiempo de CPU a lo largo de dos frames consecutivos (4 centésimas de segundo en PAL y 3,333... en NTSC), con lo que he liberado muchísimo tiempo de CPU para poder analizar los choques de los disparos contra los QBIQS que vienen bajando. La rutina de detección de marcos se realiza en cuatro fases, de las cuales están testeadas ya las tres primeras y la última queda a la espera de ser codificada y comprobada. Visualmente todo es igual. Internamente, el cambio más sustancial consiste en que al acelerar el scroll ya no va pixel a pixel, sino de dos en dos. Cosa que apenas se nota (trampantojos nuevamente).

Con un poco de suerte (y un mucho de esfuerzo programando) este fin de semana los QBIQS se podrán borrar de la pantalla, con lo que el motor de juego estará prácticamente terminado, al menos en el modo de un jugador. Me da miedo mirar qué ha pasado en el modo de dos jugadores tras toquetear todo, aunque no debería haber mucho afectado. Veremos qué puedo llevar a la RetroMadrid...

lunes, 18 de febrero de 2008

QBIQS (III) Estado del desarrollo

Bien, ya he comentado de qué va el juego y un poquito del desarrollo, pero aún no he comentado como va el desarrollo del mismo, así que en este post iré mostrando las diferentes actualizaciones, incluyendo datos del tamaño de los gráficos y músicas...

Gráficos

ConceptoDescomprimidoComprimido
1P-ST00960 bytes373 bytes
1P-ST01864 bytes282 bytes
1P-ST02704 bytes215 bytes
1P-ST03976 bytes331 bytes
1P-Marcadores608 bytes268 bytes
2P768 bytes136 bytes
SCROLL5312 bytes1348 bytes
Naves, disparos816 bytes424 bytes
TOTAL11008 bytes3377 bytes

Música

ConceptoDescomprimidoComprimido
MUSICA003059 bytes1131 bytes
REPLAYER1847 bytes1435 bytes
TOTAL4906 bytes2566 bytes

  • Tamaño real de la ROM: 10173 bytes
  • Memoria RAM utilizada: 12877 bytes
Fecha de los datos: 18 de Febrero de 2008

viernes, 15 de febrero de 2008

QBIQS (II) La historia

La historia es importante

Todo videojuego tiene que tener una historia, bueno, no es obligatorio pero los hace algo más interesantes. QBIQS no iba a ser menos. En el QUARTH la historia nos sitúa en el espacio exterior, desde donde se aproximan unas extrañas piezas que amenazan con bloquear la luz solar que llega a la Tierra. No quería que la historia del QBIQS fuese la misma, así que estuve pensando dónde podía ambientarla y por qué extraños motivos esas piezas amenazaban al pobre jugador.

La historia detrás de la pantalla

Y nunca mejor dicho. Toda la acción se desarrolla no fuera de la Tierra, es más, tampoco fuera de la pantalla del monitor. La acción se desarrolla en el interior del tubo de rayos catódicos de un monitor. Además, esto da la excusa perfecta para la decoración del juego ya que todo está ambientado con mosaicos en rojo, verde y azul (RGB que son, además, las iniciales de los tres protagonistas). Aunque no es definitiva, aquí está la historia que ambienta el juego:

¡Los monitores CRT están en peligro! Unas partículas llamadas QBIQS, que se forman en los tubos de rayos catódicos, impiden que los rayos incidan en la pantalla para formar las imágenes. Sólo tres valientes héroes son capaces de limpiar su camino. Toma el papel de Ray, Glow y Beam y limpia el tubo. ¿Sucumbirás ante el poder de los QBIQS?

jueves, 14 de febrero de 2008

QBIQS (I) Los orígenes

Un proyecto largamente pensado

Para empezar a contar la historia de este juego, permitidme parafrasear a la inmortal Sophia Petrillo: "Madrid, 1990...".

Madrid, 1990, concretamente un tórrido mes de julio de 1990, momento en el que cayó en mis manos el número 65 de la revista MSX-Club. En ese número publicaron unos cromos a color con imágenes del QUARTH para MSX2. Me quedé absolutamente fascinado con aquel juego, de apariencia tan simple y adictiva. En aquellos momentos yo sólo tenía un MSX1, por lo que no podía disfrutar de ese juego, por lo que se me metió en la cabeza la idea de programar una versión para MSX1.

Pero en aquellos momentos mis conocimientos sobre programación y sobre el MSX eran escasos, aparte de no tener los medios necesarios como para acometer semejante tarea. Un año y medio más tarde me compré un Amiga 500, por lo que el proyecto de hacer un QUARTH para MSX1 quedó en el olvido durante más de 15 años.

Una vez más he de agradecer a mi amigo Edu Robsy por idear y organizar la MSX-Dev, ya que me proporcionó la excusa perfecta para retomar este proyecto que siempre había estado rondando por mi memoria.

Comenzando punto por punto

Mi sorpresa fue mayúscula cuando, investigando un poco, descubrí la existencia de una versión del QUARTH para MSX1, llamada Block Hole y publicada por Zemina. Lógicamente el primer paso fue buscarla para ver si merecía la pena el esfuerzo de crear una nueva versión de un juego ya existente. Encontré el juego y lo probé. Me encantó el efecto de las estrellas que se desplazan pixel a pixel por debajo de las piezas, aunque éstas se desplazan carácter a carácter (igual que en la versión de 2 jugadores del QUARTH de Konami).

Sin embargo la jugabilidad de la versión de Zemina no es la del QUARTH ni mucho menos.

Así pues me decidí a rescatar el proyecto de hacer un QUARTH para MSX1 y me planteé la posibilidad de que el scroll de las piezas fuese pixel a pixel, ¿sería posible hacerlo en un MSX1? En el juego original los artistas de Konami aprovechan el registro de scroll vertical por hardware que tienen los MSX2 y superiores. De esta forma consiguen ese efecto, pero a costa de que el decorado también se desplaza junto con las piezas. En MSX1 había que hacerlo de otra manera...


La idea detrás del scroll del QBIQS es bastante simple: todas las piezas del juego se componen de 14 diferentes patrones que se combinan de manera adecuada junto con un decimoquinto patrón (el vacío). Sólo tenía que dibujar las combinaciones verticales de dos cualesquiera de estas piezas desplazándolas pixel a pixel, así tendría todos los posibles casos. Problema: 15 patrones dan 225 combinaciones, que a 8 pixels de altura por patrón dan 1800 posibles gráficos... ¡Pero si sólo tengo 256!

Analizando un poco más el problema me di cuenta de que no todas las combinaciones de patrones son válidas, por lo que calculé (por la cuenta de la vieja) aquellas que sí lo eran y todo se redujo a un total de 183 patrones. Dejé reservados 200 patrones por si (que va a ser que sí) me hacían falta más y me puse manos a la obra para ver aquello moviéndose.

Una vez dibujados los patrones, el siguiente paso fue diseñar una rutina que se encargase de volcarlos de forma adecuada en la tabla de nombres, así conseguiría el efecto visual de un scroll pixel a pixel. La cosa, que parece complicada, no lo es en absoluto, ya que con 8 tablas de 256 bytes el volcado se agiliza un montón. Me llevó toda una tarde el diseñar esas tablas (en depurarlas tardé un poco más de tiempo pues con el paso de las semanas iban saliendo los errorcillos que había), pero mereció la pena porque las piezas ya se desplazaban pixel a pixel y con la suficiente soltura como para que el juego funcione sin problemas a 60hz.

Terminaré esta introducción al QBIQS comentando que en el QUARTH original, las piezas tienen un color diferente en cada nivel, por lo que programé una rutina que permite cambiar los nibbles de una tabla en ram (aquí descubrí lo útiles que resultan para estos menesteres las instrucciones RRD y RLD). Si utilizamos esa rutina sobre una tabla de colores conseguimos el efecto deseado.

Continuará...

lunes, 11 de febrero de 2008

PONG512 (II) El desarrollo

Buenas de nuevo:

A continuación voy a destacar aquellos detalles que considero más interesantes sobre el desarrollo del juego, al mismo tiempo que hago referencias al código fuente del mismo por lo que, si estás interesado en conocer el funcionamiento interno de algunas rutinas, te recomiendo que te descargues el código y leas lo siguiente con el código al lado.

El desarrollo

Al principio todo fue muy rápido, ya que en apenas una semana ya tenía listo el engine de cambio aleatorio de tableros (ver código fuente, etiqueta RANDOM) y las dos paletas moviéndose bajo la acción de los cursores y joysticks (rutinas MOVEPLAYER1 y MOVEPLAYER2).

Hacer que la pelota rebotase contra las paletas también fue sencillo. Tanto las paletas como la pelota son sprites por hardware y, por su movimiento relativo, sólo pueden existir choques entre una paleta y la pelota. El hardware y la BIOS del MSX hacen muy bien el trabajo de detectar colisiones de sprites, dejando en la posición F3E7h el valor del registro de estado del VDP. Si el quinto bit está activo se ha producido un choque con una paleta, momento en el que hay que cambiar el sentido de la velocidad horizontal de la pelota (de esto se encarga la rutina BATREBOUND).

Pero no basta con invertir el sentido de la velocidad horizontal, ya que es en este punto cuando el ángulo de la velocidad cambia, según la posición relativa de la paleta y la pelota en el momento del choque. Aquí debo agradecer a mi amigo WYZ que me cediera amablemente la rutina que él había usado en su propia versión del PONG (el acceso a la tabla de ángulos se encuentra, también, en la rutina BATREBOUND).

Una vez llegados a este punto y con la rutina de movimiento de la pelota (MOVEBALL) que trabaja en aritmética de punto fijo (8 bits de parte entera y 8 de parte decimal para conseguir un movimiento más suave), ya se podía jugar a darle golpes a la pelotita... la cual seguía empeñada en atravesar los muros.

Detección de colisiones con los muros

Esta fue la rutina que más tiempo me llevó, ya que quería que resultara lo más aproximada a la realidad. La versión que está incluída en el juego (bajo la etiqueta WALLREBOUND) es la tercera que intenté, ya que las dos primeras siempre daban problemas en las esquinas.

La idea fundamental consiste en comprobar si se ha de cambiar el signo de alguna de las componentes de la velocidad de la pelota (horizontal y vertical). Para ello se comprueba si el carácter bajo la pelota en ese momento (que se obtiene mediante la rutina GETCHAR) la hace rebotar o no. En el peor de los casos, la pelota puede estar sobre cuatro caracteres diferentes, por lo que se calculan las coordenadas de las cuatro esquinas del sprite que la representa, pero un pixel hacia el interior de la pelota (para así simular que es redonda y no cuadrada). Se obtiene el carácter bajo esa posición y se realizan los cálculos para comprobar si hay que hacer un rebote o no.

La rutina WALLREBOUND, que realiza este trabajo, está dividida en cuatro rutinas bastante similares: @@UL, @@UR, @@LR y @@LL, que comprueban, respectivamente las esquinas superior izquierda, superior derecha, inferior derecha e inferior izquierda. Fue aquí donde eché de menos tener más registros, por lo que tuve que echar mano del conjunto alternativo de registros del Z80, utilizando el par BC' para incrementar o decrementar el signo de las componentes horizontal y vertical de la velocidad de la pelota.

Cada vez que un carácter bajo la pelota forma parte de un muro, se incrementa o decrementa b' y c' para indicar cuál sería la velocidad relativa de la pelota tras chocar contra ese carácter de forma individual. Este cálculo se repite para los cuatro caracteres y al final los signos de b' y c' son, respectivamente, los signos de las componentes horizontal y vertical de la nueva velocidad. Veamos como funciona el algoritmo al extenderlo a más de un carácter:

Si los caracteres que se encuentran bajo la pelota son A, B, C y D (para las esquinas superior izquierda, superior derecha, inferior izquierda e inferior derecha respectivamente), entonces el signo de la velocidad según los choques es el siguiente:

  • Choques contra un único carácter:
    • A -> +x, +y
    • B -> -x, +y
    • C -> +x, -y
    • D -> -x, -y
  • Choques contra dos caracteres:
    • AB -> 0, +2y
    • CD -> 0, -2y
    • AC -> +2x, 0
    • BD -> -2x, 0
  • Choques contra tres caracteres
    • ABC -> +x, +y
    • ABD -> -x, +y
    • ACD -> +x, -y
    • BCD -> -x, -y
Es fácil ver que la extensión del algoritmo funciona cuando la pelota choca contra uno, dos o tres caracteres. Si no chocase contra ninguno, tanto b' como c' serían iguales a 0, lo que significa que no hay que variar la velocidad. Esto también sucede si los cuatro caracteres forman parte de un muro, cosa que, por construcción del tablero y del juego, es imposible. Por lo tanto el algoritmo de detección de choques contra los muros funciona perfectamente.

El resto del programa no tiene mayor complicación, ya que es bastante fácil de seguir. El bucle principal se limita a llamar a las rutinas en orden. La única complicación es seguir el flujo del programa cuando se marca un gol, ya que se llama a una subrutina que se encarga de reinicializar todos los valores para volver a las posiciones iniciales de las paletas y la pelota, para finalmente retornar al bucle principal y continuar la partida.

También la zona de datos puede resultar algo confusa, porque para ahorrar tamaño, muchos de los datos son compartidos por más de un concepto (como es el caso de los tres últimos bytes de la definición del carácter del muro, que también son la definición del muro completo sin agujeros).

Descarga

En breve estará disponible una zona de descargas donde se podrán descargar todos los juegos, utilidades y códigos que vayan apareciendo en el blog... sed pacientes :D

martes, 5 de febrero de 2008

PONG512 (I) La historia

¿Cuánto pueden dar de sí 1024 bytes?

En realidad, 1024 bytes dan para bastante más de lo que se podría pensar en un principio. Esta historia comienza en julio de 2005, cuando a los locos de Karoshi les dió por organizar un concurso para programar un clon del PONG en, como máximo, 1KB (1024 bytes).

Este minijuego es el resultado de mi participación en dicho concurso, así como mi primer juego para MSX (después de todos los que hice de pequeño en BASIC, aunque esos no salieron de mi habitación).

Remontándose a los orígenes

Personalmente guardo muy buenos recuerdos de mi infancia con respecto al PONG, recuerdos que me incluyen a mí y a mi padre jugando en el bar de unos amigos con una de esas maquinitas de PONG que se conectaban a la tele. Como una de mis motivaciones para programar videojuegos es intentar que todo lo relacionado con los juegos clásicos sea heredado por las siguientes generaciones (acostumbradas ya a los chorrocientosmil polígonos texturizados moviéndose al mismo tiempo en la pantalla) me animé a refrescar mis conocimientos del ensamblador del Z80 y a programar un clon del que se considera el pionero de los videojuegos, aunque puede que no lo sea. Veamos un poquito de historia:

Según el criterio que sigamos, podemos encontrar a dos padres de los videojuegos. El verdadero padre se considera que fue Steve Russel, quien programó el primer juego de ordenador, Space Wars, que funcionaba en un PDP-1.

Sin embargo, el padre "legal" de los videojuegos fue Ralph-Baer, un alemán educado en los Estados Unidos. Fue él quien desarrolló y patentó los primeros videojuegos que se podían conectar a un televisor en blanco y negro. Un gran inventor, pero un mal vendedor. Su patente terminó en 1970 y un par de años después Philips convirtió el juguete de Baer en la primera consola doméstica de la historia: la Magnavox Odyssey.

En 1971 Nolan Bushnell hizo una versión del Space Wars, juego que conoció y jugó en su época de estudiante, para ser jugada en lugares públicos. Sin embargo Computer Space no tuvo el éxito esperado y fue reemplazado por un juego mucho más simple, mucho más adictivo y mucho, pero muchísimo más conocido: el PONG.

Cabe mencionar la anécdota que rodea a la primera máquina de PONG de la historia, que fue devuelta desde el bar Andy Capp's porque creían que era defectuosa, cuando en realidad estaba bloqueada pues el monedero se había llenado de monedas y no aceptaba más.

El concepto del juego

Un juego muy simple a la par que adictivo, dos factores que me terminaron de convencer para coger el ensamblador y ponerme a teclear instrucciones como un loco. Pero tras más de 14 años sin haber tocado el ensamblador del Z80 la tarea no iba a ser sencilla.

Una costumbre que tengo (mala o buena, eso aún no está claro) es que no me pongo a tirar una sola línea de código hasta que no tengo muy claro el concepto del programa completo o de alguna parte significativa del mismo. Si no lo hago así luego me toca retocar bastante y ya se sabe que no es fácil controlar los bugs cuando andas cambiando diferentes partes del código.

Ya desde el primer momento se me ocurrió no hacer una versión totalmente fiel a la idea original, puesto que quería añadir algún tipo de funcionalidad extra que le diese una nueva dimensión al juego, sin cambiar por completo el espíritu del mismo. La idea definitiva surgió al recordar alguna versión del PONG que se conectaba a la televisión, concretamente aquellas que venían con varios tableros que se presentaban como juegos diferentes.

Así pues nació la idea de que los tableros no fueran estáticos, sino que pudieran sufrir alteraciones durante el juego. Los muros laterales eran sencillos, ya que aparecerían diversos tipos de barreras que dificultarían la tarea de marcar un gol, pero algo tenía que hacer con los muros superior e inferior. Nada más simple: si sincronizaba ambos muros podía hacer que la pelota desapareciera por arriba para reaparecer por abajo (y viceversa). Había nacido el concepto inicial del PONG512.

¿Qué se esconde tras el nombre?

PONG512 es, ante todo, un PONG, pero al mismo tiempo se ha de distinguir del original por el tema de los tableros cambiantes. Hay cuatro muros que pueden cambiar de forma, aunque como dos de ellos están sincronizados, en realidad podemos contar tres muros que cambian. Si cada uno de ellos tiene 8 posibles formas, en total hay 8x8x8=512 combinaciones de tableros.

Instrucciones

¿Existe alguien que en algún momento de su vida no haya echado una partida al PONG o a alguno de sus derivados? Para jugar a PONG512 hay que seguir el mismo mecanismo: controlar tu paleta moviéndola verticalmente para colarle la pelota al contrario y evitar que él te la cuele a tí. El cambio de tableros se realiza simplemente pulsando el botón, momento en el que alguno de los muros cambiará aleatoriamente... o no... ¡quién sabe!

El jugador 1 puede jugar con los cursores y la barra de espacio o con el joystick en el puerto 1. El jugador 2 jugará siempre con el joystick conectado al puerto 2. Mientras la pelota se encuentra en movimiento se puede salir del juego sin más que pulsar la tecla ESC, lo que nos devolverá al BASIC del MSX.

Continuará...

domingo, 3 de febrero de 2008

Comenzamos...

Hola a todos,

Pues eso, comenzamos aquí con este blog sobre programación de videojuegos para ordenadores antiguos. Concretando un poco más: programación de videojuegos para MSX.

Hace más de 20 años que mis padres me sorprendieron unas navidades con uno de los mejores regalos que he recibido en toda mi vida: mi primer ordenador. Desde aquel día he ido aprendiendo sin parar y metiéndome en el mundo de la informática, hasta tal punto que hoy me da de comer esa afición que llevaba dentro de mí.

Espero que os resulte de interés todo lo que aparezca por aquí, en ocasiones pecaré de superficial y en otras de excesivamente técnico, pero intentaré que los contenidos sean lo más interesantes posible.

Saludos