¡¡400 ejemplares vendidos!!

El libro “Programación Retro del Commodore 64” se publicó en Amazon el 30 de junio:

Libro

Desde entonces, y en apenas tres meses y medio, se han vendido más de 400 unidades. Un auténtico logro que me hace muy feliz y que supera de largo mis mejores expectativas 🙂 .

Pero no sólo eso. Es que además las valoraciones de los lectores en Amazon son excelentes. A día de hoy, el libro tiene 30 valoraciones de clientes, todas ellas de 5 estrellas:

Opiniones clientes 9

Muchísimas gracias a todos por el interés y por el apoyo 🙂 .

 

Asteroids: una explosión en condiciones

Como adelantamos en la entrada anterior, no es posible pasar toda la animación de la explosión en un único ciclo del bucle de juego. Si se hace sin bucle de demora, entonces la explosión apenas es perceptible. Y si se hace con bucle de demora, parece que el juego se “engancha” en las explosiones.

Por tanto, la solución es también la ya adelantada: meter un retardo en las explosiones, es decir, cambiar de “frame” cada N ciclos del bucle de juego.

Para hacer esto nos hace falta:

  • Definir una nueva variable “jugadorActivo”, de modo que podamos distinguir si el jugador está activo ($01), es decir, moviéndose y disparando, o si está explotando ($00).
  • Definir una nueva variable “jugadorRetardoExplosion” de modo que, si el jugador está explotando, en cada ciclo del bucle de juego se va descontando uno y, al llegar a cero, se avanza el “frame” de la explosión. Esto mismo ya lo hicimos con los giros y los disparos de la nave.
  • Definir una nueva variable “jugadorExplosionFrame”. Esta variable nos servirá para llevar cuenta de por qué “frame” de la explosión vamos y, junto con “jugadorRetardoExplosión”, nos permitirá llevar el control de la animación.
  • Definir una nueva variable “jugadorVidas”. Hasta ahora no la habíamos definido, pero en todos los juegos el jugador tiene una serie de vidas y, al producirse determinados eventos –colisiones con asteroides en nuestro caso– éstas se van descontando.

Todo esto podemos verlo en la nueva sección de variables del jugador:

Asteroids - Nuevas variables explosión

Pues bien, si ahora el jugador puede estar activo ($01) o explotando ($00), la actualización del jugador será diferente en uno y otro caso:

Asteroids - Nueva actualización jugador

Es decir, si el jugador está activo ($01) haremos lo mismo que hasta ahora (actualizar su posición, tomar nota de sus disparos, y detectar sus colisiones). Ahora bien, si el jugador está explotando ($00), lo que haremos será animar la explosión.

Al empezar la partida el jugador nace activo ($01), y la transición a la situación de explotando se produce en la rutina “actualizaColisionesJugador”, que ahora tiene una codificación diferente:

Asteroids - Detectar colisiones

Efectivamente, si recordáis de la entrada anterior, cuando la rutina “actualizaColisionesJugador” detectaba una colisión mediante el registro SPSPCL = $d01e, inmediatamente hacía la animación llamando a “jsr animaExplosion”. Ahora, sin embargo, se limita a cambiar el estado del jugador (lo pone a $00), y delega en la rutina “actualizaJugador” ya vista para que ésta haga lo que corresponda a esa situación. Y lo que corresponde es, nuevamente, ejecutar la animación.

Por último, la rutina “animaExplosion” también cambia, ya que es aquí donde se utiliza el nuevo retardo que va a permitir cambiar el “frame” de la explosión cada N ciclos del bucle de juego:

Asteroids - Nueva anima explosión

Como se puede observar, ahora una llamada a la rutina “animaExplosion”, en principio, sólo reduce la variable “jugadorRetardoExplosion” y termina (“rts”). Ahora bien, cuando esa variable llega a cero, entonces sí, el retardo vuelve a ponerse a cinco y, además, se avanza el “frame” de la animación. Para saber por qué “frame” vamos y cuál corresponde ahora, utilizamos la variable “jugadorExplosionFrame”.

Pero la rutina anterior no está completa, porque cuando termina la animación, es decir, cuando ya se han pintado los tres “frames” de la explosión, llama a una sección de la rutina “animaExplosion” etiquetada con “aeFinAnimacion”:

Asteroids - Fin animación.PNG

Por tanto, al terminar la animación de la explosión, lo que hacemos es:

  • Decrementar en uno las vidas del jugador. Por cierto, ahora las vidas del jugador también se muestran en pantalla (ver rutina “actualizaPantalla”). Lo que no hacemos todavía es controlar si las vidas han llegado a cero; lo dejamos para un poco más adelante.
  • Volver a poner la variable “jugadorExplosionFrame” a cero, de modo que, si el jugador vuelve a colisionar, volvamos a empezar la animación de la explosión desde el primer “frame”.
  • Volver a poner al jugador como activo ($01), para que pueda volver a moverse y disparar.
  • Y volver a leer el registro de colisiones SPSPCL. Esto es un truco y necesita explicación. Si no lo hacemos, siempre que la nave colisiona con un asteroide, la aplicación detecta dos colisiones seguidas y descuenta dos vidas (podéis probarlo comentando “lda SPSPCL” en la línea 549). La forma de evitarlo es leyendo el registro SPSPCL por segunda vez al final de la animación. Recordemos que la lectura de este registro lo borra.

Y llegados a este punto (el final de la animación de explosión), podríamos volver a poner el juego en la situación inicial (nave en el centro de la pantalla, pocos asteroides, etc.), o continuar desde la situación actual no tocando nada más. Hemos optado por esto último.

Y ahora ya sí, por fin, podemos explotar a gusto:

Asteroids - Explotar a gusto

En la versión 12 del proyecto podéis apreciar los avances.


Código del proyecto: Asteroids12

Asteroids: colisiones de la nave con los asteroides

Ya tenemos una nave que se mueve y dispara y muchos asteroides. El siguiente paso será detectar la colisión de la nave con los asteroides. La colisión de los asteroides entre sí no la tendremos en cuenta.

En caso de que la nave colisione con los asteroides, lo que haremos será animar una explosión y reducir las vidas de la nave o jugador.

Por tanto, lo primero es diseñar la explosión. La explosión no es más que una secuencia de “frames” que diseñaremos con el editor de sprites de CBM prg Studio. No hace falta definir otro fichero de sprites. El mismo fichero “Sprites.spt” que hemos utilizado hasta ahora nos vale, puesto que cargaremos todos los “frames” (8 x nave, 1 x asteroides y 3 x explosión) uno detrás de otro en memoria.

El diseño de la explosión puede verse aquí (“frames” 10, 11 y 12):

Asteroids - Explosión

Como en ocasiones anteriores, habrá que exportar los doce “frames” al fichero “Sprites.bin” e importarlo desde “Recursos.asm”. Como esto no es nada nuevo, no abundaremos más en ello.

Sí es importante, en cambio, ampliar las variables del jugador, de modo que definamos una nueva tabla “explosionFrames” para referenciar con comodidad los “frames” de la explosión (201 – 202), igual que hiciéramos en su momento con los “frames” de la nave (192 – 199):

Asteroids - Frames explosión

Ahora hay que detectar las colisiones y reaccionar ante ellas. Pero lo primero es detectarlas. Para ello dotamos una nueva rutina “actualizaColisionesJugador”, que deberemos llamar desde la actualización del jugador:

Asteroids - Actualiza colisiones jugador - Llamada

Y lo que hace la rutina “actualizaColisionesJugador” es más o menos sencillo:

Asteroids - Rutina actualiza colisiones

Es decir:

  • Lee el registro SPSPCL = $d01e, que es el registro que señaliza las colisiones entre sprites.
  • Analiza si el bit 0 de SPSPCL está activo. En caso de estar activo, ello significa que la nave (sprite 0) se ha visto involucrada en una colisión con otro sprite (necesariamente un asteroide, porque no hay más sprites).
  • En caso afirmativo, es decir, si la nave ha chocado con un asteroide, llama a la rutina “animaExplosion”, que es la encargada de hacer la animación.

Por su parte, la nueva rutina “animaExplosion” de momento es sencilla. En entradas posteriores la complicaremos. Hace esto:

Asteroids - Anima explosión

Es decir:

  • Utilizando el registro Y como índice, va leyendo las entradas de la tabla “explosionFrames”, es decir, va leyendo los punteros a los “frames” de la explosión (“lda explosionFrames,y”).
  • Utilizando la rutina “configuracionBasica”, va cambiando el puntero de la nave o, lo que es lo mismo, va cambiando su “frame”. Esto mismo se podría hacer con un simple “sta SPRITE0” (SPRITE0 = $07f8).
  • Repite lo anterior hasta haber recorrido todos los frames de la animación.

Si probáis esto, veréis la animación de la explosión es prácticamente imperceptible. ¿Cuánto puede tardar un súper C64 en hacer un bucle de tres iteraciones y además en código máquina?

Total, tenemos que meter un bucle de demora como éste, ¿no?

Asteroids - Bucle demora

A estas alturas ya deberíamos saber que un bucle de demora como ese no funciona, porque no sólo ralentiza la animación / explosión, sino que ralentiza todo el juego. Ahora parece que el juego se engancha en la explosión:

Asteroids - Explosión enganchada

La solución es la misma que aplicamos a la nave y los disparos: un retardo para la explosión. Es decir, una nueva variable “jugadorRetardoExplosion” que, partiendo de un valor N (típicamente 5), va descontando uno en cada iteración del bucle de juego, y, cuando llega a cero, produce el cambio de “frame”, además de volver a ponerse al valor inicial N. Así hasta pasar todos los “frames” de la animación.

De este modo, la presencia en pantalla de cada “frame” de la explosión se prolonga durante N ciclos del bucle de juego, en vez de hacer pasar todos los “frames” (la animación completa) en un único ciclo. Y sin ralentizar nada más.

Lo anterior, a su vez, nos lleva a la conveniencia de definir una nueva variable “jugadorActivo” (el lector atento la habrá visto aparecer, aunque comentada, unas cuantas imágenes más arriba) que, además de ser análoga a “disparosActivos” o “asteroidesActivos”, nos permitirá controlar si el jugador está vivo ($01), en cuyo caso hay que moverlo y actualizarlo, o si el jugador está explotando ($00), en cuyo caso sólo hay que animar una explosión.

Pero todo esto lo describiremos en detalle en la entrada que sigue, así que de momento comentamos el bucle de demora, documentamos algunos comentarios, y nos despedimos hasta que podamos explotar en condiciones 🙂 .

Asteroids - Bucle demora comentado

En la versión 11 del proyecto podremos ver todo lo descrito.


Código del proyecto: Asteroids11

Asteroids: activación de nuevos asteroides

En las entradas anteriores hemos diseñado los asteroides, los hemos inicializado y los hemos movido. Y como en las estructuras de datos, concretamente en la tabla “asteroidesActivos”, sólo aparecían dos asteroides activos ($01), por la pantalla sólo se movían dos asteroides:

Asteroids - Dos asteroides activos

Lo que vamos a hacer ahora es:

  • Modificar la situación inicial para que el juego empiece con un único asteroide activo.
  • Modificar la actualización de los asteroides para que, además de moverlos, cada vez haya más asteroides activos.

Modificar la situación inicial para empezar con un único asteroide es sencillo. Llega con modificar los valores iniciales de la tabla “asteroidesActivos” para que sólo un asteroide, el primero, esté activo ($01) al comenzar el juego:

Asteroids - Solo 1 asteroide activo

Modificar la actualización de los asteroides para que cada vez haya más asteroides activos implica hacer dos cosas. La primera es ampliar la actualización de asteroides para llamar a la nueva rutina “actualizaAsteroidesActivos”:

Asteroids - Actualización asteroides ampliada

Y la segunda es dotar la nueva rutina “actualizaAsteroidesActivos”, que es la que se encargará de ir activando asteroides poco a poco (esto de “poco a poco” lo matizaremos más adelante):

Asteroids - Rutina actualiza asteroides activos

Esquemáticamente, lo que hace la rutina “actualizaAsteroidesActivos” es:

  • Recorre la tabla “asteroidesActivos” usando el registro Y como índice.
  • Si el asteroide está activo ($01), continúa con el siguiente asteroide.
  • Si el asteroide no está activo ($00), lo activa y termina con “rts”.
  • Si ha recorrido toda la tabla, termina con “rts”.

Ahora bien, en la imagen / esquema anterior no aparece el detalle de lo que hay que hacer para activar un asteroide. En la rutina sólo aparecen un comentario y unos puntos suspensivos (“; …”) que en breve pasaremos a detallar.

Pero antes de entrar en los detalles de la activación, vamos a recordar lo que es el Jiffy Clock y cómo puede utilizarse para generar información aleatoria o pseudo-aleatoria.

El Jiffy Clock es un reloj similar al TOD, siendo la principal diferencia que el reloj TOD está actualizado por hardware (hay unos registros que mantienen y actualizan los valores de las horas, minutos, segundos y décimas), mientras que el Jiffy Clock está actualizado por software (interrupciones). El Jiffy Clock utiliza las posiciones $a0, $a1 y $a2, siendo $a0 la posición que cambia más despacio, y $a2 la que cambia más rápido.

El Jiffy Clock, igual que el TOD, puede usarse para muchas aplicaciones (relojes, cronómetros, alarmas, etc.) y, entre ellas, para generar valores aleatorios. Para ello, llega con leer el valor de cualquiera de sus posiciones. Si se quiere un valor que cambie rápido con el tiempo, lo más lógico es utilizar la posición más rápida: $a2.

Aclarado lo que es y para qué sirve el Jiffy Clock, volvemos a la activación de asteroides. Para activar un asteroide hay que hacer todo lo siguiente:

Asteroids - Activar asteroides

Es decir, la rutina “actualizaAsteroidesActivos”, a la hora de activar un asteroide:

  • Lee el valor de la posición $a2 del Jiffy Clock y lo guarda como coordenada X del asteroide.
  • Vuelve a leer el valor de la posición $a2 y lo guarda como coordenada Y del asteroide. El motivo de volver a leer $a2, en vez de reutilizar el valor recientemente leído para X, es permitir que surjan valores diferentes para X e Y, si es que $a2 hubiera podido cambiar en tan breve lapso de tiempo.
  • Hace lo mismo con la velocidad del asteroide. Ahora bien, dado que $a2 puede tomar (en principio) cualquier valor arbitrario, y las velocidades queremos que oscilen entre 0 y 3, como con la nave, lo que hacemos es $a2 AND %00000011. De este modo, el valor resultante tiene que estar entre 0 y 3 = %00000011.
  • Hace lo mismo con el ángulo del asteroide. Ahora bien, dado que los ángulos queremos que oscilen entre 0 y 7, como con la nave, lo que hacemos es $a2 AND %00000111. De este modo, el valor resultante tiene que estar entre 0 y 7 = %00000111.
  • Hace lo mismo con la expansión del asteroide. Ahora bien, dado que la expansión sólo puede tomar dos valores, sí ($01) o no ($00), lo que hacemos es $a2 AND %00000001. De este modo, el valor resultante tiene que estar entre 0 y 1 = %00000001.
  • Todos estos valores (X, Y, velocidad, ángulo y expansión), además de generarlos de forma aleatoria a partir del Jiffy Clock, lógicamente se almacenan en las tablas correspondientes.
  • Por último, en el caso particular de la expansión, no es suficiente con actualizar la tabla “asteroidesExpansion”. Además, hay que modificar la expansión horizontal / vertical del sprite. Esto se podría hacer modificando directamente los registros XXPAND = $d01d e YXPAND = $d017 pero, por aquello de reutilizar algo de código y hacerlo igual que en la inicialización, volvemos a hacer uso de la rutina “configuracionAvanzada”. Esto sólo es necesario hacerlo, claro está, si en la ruleta del Jiffy Clock el asteroide ha resultado agraciado con la expansión; en caso contrario no es necesario y la rutina termina con “rts”.

Y ahora vamos con el “poco a poco”. Más arriba hemos dicho que la rutina “actualizaAsteroidesActivos” iba activando los asteroides “poco a poco”. Sin embargo, si ejecutamos el juego casi instantáneamente vemos siete asteroides:

Asteroids - Todos asteroides activos

Al decir que la rutina actuaba “poco a poco” nos referíamos a que no activa todos los asteroides de golpe, en una única ejecución. Activa un asteroide y termina. Activa otro asteroide y termina. Y así sucesivamente hasta que los siete asteroides están activos.

Ahora bien, el código máquina es tan rápido que, según ejecutamos el juego, y casi sin darnos cuenta, ya tenemos los siete asteroides activos.

Pero este activar y terminar, activar y terminar, activar y terminar, aunque de momento no le veamos utilidad, lo que hace es preparar la rutina para más adelante. Porque más adelante, en vez de ejecutar esta rutina en cada ciclo del bucle de juego, o de forma periódica, lo que haremos será ejecutarla vinculadamente a algún evento del juego. Por ejemplo, cuando el usuario haya alcanzado X puntos o un determinado nivel. Y, entonces sí, los asteroides se irán activando poco a poco o de vez en cuando.

Todo lo descrito puede verse en la versión 10 del proyecto. Y en la próxima entrada empezaremos con las colisiones.


Código del proyecto: Asteroids10

Asteroids: movimiento de los asteroides

El movimiento de los asteroides también es parecido al de la nave. Los asteroides pueden moverse hacia arriba, hacia abajo, hacia la izquierda, hacia la derecha, y en diagonal. Pero a diferencia de la nave, los asteroides no tienen un motor ni un piloto con un joystick, así que, una vez que se mueven en una dirección, no pueden cambiarla salvo que se destruyan chocando contra la nave o con un disparo.

Ya lo decía Newton en su primera ley del movimiento o “ley de inercia” que, como todos sabemos, es de aplicación directa a los videojuegos: “Todo cuerpo continúa en su estado de reposo o movimiento uniforme en línea recta, no muy lejos de las fuerzas impresas a cambiar su posición” 🙂 .

Asteroids - Sir_Isaac_Newton.jpg

Bueno, hemos empezado hablando directamente de “movimiento”, pero en realidad deberíamos hablar de “actualización”. Lo que ocurre es que, al igual que pasaba con el jugador / nave en las primeras entradas, de momento actualizar es lo mismo que mover:

Asteroids - Actualizar asteroides

Más adelante, actualizar los asteroides implicará hacer más cosas, como detectar si han colisionado con disparos (y, por tanto, hacerlos explotar y desactivarlos), o ir activando nuevos asteroides según pasa el tiempo o según el jugador va ganando puntos.

Pero de momento, actualizar es lo mismo que mover, es decir, hacer todo esto (para cada asteroide activo; es un bucle):

Asteroids - Mover asteroides

Es decir, para cada asteroide activo hay que (nuevamente, de forma muy similar a como hacíamos con la nave):

  • Calcular su nueva posición en función de su posición actual, su velocidad, y su ángulo.
  • Verificar los límites de la pantalla, es decir, verificar si la nueva posición está dentro o fuera de la pantalla y actuar en consecuencia. De momento, e igual que pasaba con la nave, sólo se verifican los límites en el sentido vertical (Y), ya que en el sentido horizontal (X) las zonas ocultas son despreciables. Téngase en cuenta que todavía seguimos limitando el movimiento de los sprites en el sentido X a ocho bits; más adelante quitaremos esta limitación.
  • Actualizar la posición del sprite.

Una cosa curiosa es que, para calcular la nueva posición del asteroide, no nos hace falta una nueva rutina “calculaNuevaPosicionAsteroides”. Nos vale la misma rutina “calculaNuevaPosicionJugador” que ya veníamos usando para el jugador. Esto es así porque las normas de la física son las mismas para todos 🙂 . Y, además, esto nos permite simplificar el código.

Lo siguiente que haremos será ir metiendo cada vez más asteroides. Hasta un máximo de siete, claro. ¡¡Esto se anima!!


Código del proyecto: Asteroids09

Asteroids: configuración y posicionamiento de los asteroides

La configuración y el posicionamiento (inicial) de los asteroides, como en el caso de la nave, tiene lugar en la rutina de inicialización, es decir:

Asteroids - Inicialización asteroides

Si se observa con detenimiento, esta rutina es muy parecida a la inicialización de la nave (posicionamiento inicial, configuración básica y configuración avanzada), siendo las principales diferencias:

  • Se ejecuta en bucle, desde 0 hasta 6, ambos incluidos. Esto es así porque tenemos hasta 7 asteroides. En una versión posterior de esta rutina cambiaremos el enfoque, y sólo inicializaremos los asteroides que estén activos ($01). Ahora mismo los inicializamos todos, los siete.
  • No hace falta configurar los colores del multicolor, puesto que estos son compartidos entre todos los sprites multicolor, y ya se están configurando al inicializar la nave.
  • En la configuración básica (activación, color y puntero), el puntero se pone apuntando a 200 (200 x 64 = 12.800 = $3200). Esto es así porque en la posición $3200 es donde se importa el “frame” de los asteroides.
  • En la configuración avanzada (multicolor, expansión y prioridad sobre los caracteres), la expansión se toma de la tabla “asteroidesExpansion”. Además, una vez que ya se han ajustado los disparos, estos se pintan por debajo de los sprites o, dicho de otro modo, se da prioridad a los sprites sobre los caracteres. Esto también se ha cambiado en “inicializaJugador”.

En definitiva, se trata de una inicialización muy parecida a la de la nave. Sólo cambian algunos detalles y, además, se ejecuta en bucle para los siete asteroides.


Código del proyecto: Asteroids09

Asteroids: los asteroides – un híbrido entre la nave y los disparos

Según dicen, la experiencia es un grado. Y en el caso de los asteroides parece que va a ser así.

Los asteroides, por un lado, se parecen a la nave. Y se parecen a la nave porque son sprites. Por tanto, se van a configurar, posicionar, mover, etc., como la nave.

Pero, por otro lado, los asteroides se parecen a los disparos. Y se parecen a los disparos porque puede haber varios (hasta siete) y, por tanto, en un momento dado los habrá activos e inactivos, y habrá que llevar el control.

Por tanto, al final tenemos un híbrido entre la nave y los disparos. Lo bueno es que ya sabemos cómo manejar la nave, y también sabemos cómo manejar los disparos. Por tanto, sólo se trata de hacer una mezcla.

Esta mezcla ya se observa en las estructuras de datos que aparecen en el nuevo fichero “Asteroides.asm”, y que no debemos confundir con el fichero “Asteroids.asm”, sin la “e”, que es el fichero principal del juego.

Estas variables son parecidas a las de la nave (número de sprite, coordenada X, coordenada Y, velocidad, ángulo, etc.) pero, por otro lado, son tablas de siete posiciones, porque podemos tener hasta siete asteroides activos:

Asteroids - Variables asteroides

Las primeras tablas no necesitan explicación, porque son totalmente análogas, pero en formato tabla, a las variables de la nave: “jugadorSprite”, “jugadorX”, “jugadorY”, “jugadorVelocidad” y “jugadorAngulo”.

La tabla “asteroidesActivos” sirve para controlar qué asteroides están activos en un momento dado. Se utiliza el mismo convenio que con los disparos: $00 = inactivo, $01 = activo.

Por último, la tabla “asteroidesExpansion” sí es nueva. Como dijimos en la entrada anterior, vamos a tener asteroides grandes (expandidos) y pequeños (sin expandir), y el tamaño de cada uno lo vamos a controlar con esta tabla. El valor $01 indica que el asteroide está expandido, tanto horizontal como verticalmente, y el valor $00 indica que no lo está.

Obsérvese como, en la versión 09 del proyecto, partimos de una situación inicial en la que sólo los asteroides uno y dos están activos. Además, el asteroide uno está expandido y el dos no lo está:

Asteroids - Dos asteroides activos

El lector puede jugar con estas tablas para cambiar el número de asteroides activos, su posición inicial, su ángulo, su expansión, etc. En versiones posteriores gestionaremos todos estos cambios en los asteroides de manera automática mediante el programa, respondiendo a los eventos del juego (colisiones, etc.), e intentando que tengan una base aleatoria.

Por lo demás, los asteroides, igual que el jugador / nave, la pantalla o los disparos, tienen:

  • Una rutina de inicialización (“inicializaAsteroides”).
  • Y otra rutina de actualización (“actualizaAsteroides”).

Estas rutinas aparecen declaradas en el nuevo fichero “Asteroides.asm” (con “e”), y se invocan desde el programa principal, es decir, desde el fichero “Asteroids.asm” (sin “e”):

Asteroids - Llamada rutinas asteroides

El contenido de estas rutinas se verá en detalle en las entradas que siguen, pero, por analogía con lo ya visto con la nave y los disparos, ya podemos imaginarnos en qué pueden consistir.


Código del proyecto: Asteroids09