Asteroids: configuración y posicionamiento de la nave

Una vez diseñada la nave con sus ocho “frames”, ya podemos configurar el sprite y pintarlo en pantalla.

Por claridad, todo el código relativo a la nave o el jugador vamos a ubicarlo en un fichero independiente llamado “Jugador.asm”. Haremos lo mismo con los asteroides, los disparos, etc.

Pues bien, como ya hemos dicho, todos los elementos del juego deben tener:

  • Una inicialización.
  • Y una actualización.

En el caso de la nave o jugador, la inicialización consistirá precisamente en la configuración y posicionamiento inicial del sprite, y la actualización consistirá en el movimiento (y más adelante los disparos y las colisiones).

Así pues, igual que antes inicializábamos la pantalla, ahora inicializamos el jugador. La llamada a la inicialización del jugador está justo después de inicializar la pantalla:

Asteroids - Pantalla de juego - Llamada

Y la inicialización consiste en:

Todo esto puede verse aquí:

Asteroids - Inicialización nave

Todo lo hacemos con las rutinas de la librería “LibSprites”. Aprovecho para adelantar que algunas rutinas de esta librería, igual que ya ocurrió con la rutina “configuraColores” de “LibChars” (que no admitía el color del borde), adolecían de algunas limitaciones.

Por ejemplo, la posición X de los sprites sólo podía oscilar entre 0 y 255 (un byte), y sólo era posible activar la expansión horizontal/vertical, pero no era posible desactivarla una vez activa. Por todo ello, ha sido necesario mejorar la librería. Pero lo iremos viendo sobre la marcha.

Bueno, pues con este código ya tenemos la pantalla en negro (esto lo conseguimos en una entrada anterior) y el sprite en pantalla:

Asteroids - Inicialización nave 2

Lo siguiente será el movimiento.


Código del proyecto: Asteroids01

Asteroids: la nave

Nuestro siguiente objetivo será diseñar el sprite de la nave.

Para diseñar el sprite utilizamos el editor de sprites de CBM prg Studio. Será un sprite multicolor con estos colores:

  • Color del sprite: gris claro ($0f).
  • Color multicolor 1: gris oscuro ($0b).
  • Color multicolor 2: amarillo ($07).

Asteroids - Nave

Inicialmente, la nave se moverá tal cual (usando como único “frame” el anterior) hacia arriba, hacia abajo, hacia la derecha y hacia la izquierda, en función de la tecla pulsada o el interruptor del joystick presionado.

Sin embargo, un poco más adelante cambiaremos el movimiento, de modo que al pulsar derecha o izquierda cambiemos la orientación o ángulo de la nave (cambiemos el “frame”) y, al pulsar arriba aceleremos, y al pulsar abajo frenemos.

Por tanto, es un buen momento para diseñar tantos “frames” como ángulos vayamos a considerar. Yo particularmente creo que con ocho ángulos es más que suficiente:

Asteroids - Angulos

De este modo, diseñamos ocho “frames” para el sprite de la nave:

  • Frame 0: Nave hacia arriba (0 grados).
  • Frame 1: Nave hacia arriba y la derecha (45 grados).
  • Frame 2: Nave hacia la derecha (90 grados).
  • Frame 3: Nave hacia abajo y la derecha (135 grados).
  • Frame 4: Nave hacia abajo (180 grados).
  • Frame 5: Nave hacia abajo y la izquierda (225 grados).
  • Frame 6: Nave hacia la izquierda (270 grados).
  • Frame 7: Nave hacia la arriba y la izquierda (315 grados).

Nosotros en el programa usaremos una variable “ángulo” que tomará los valores desde el 0 hasta el 7, pero el editor de sprites numerará los “frames” desde el 1 hasta el 8. En realidad, el editor de sprites ni siquiera maneja el concepto de “frame”; para él todo son sprites (diseños) que se pueden exportar como bloques de 64 bytes. Es cuestión del programador utilizar esas definiciones de 64 bytes para el mismo sprite, en plan animación, o para sprites diferentes.

El frame 0 (hacia arriba) hay que diseñarlo desde cero. Diseñar el frame 4 (hacia abajo) a partir del frame 0 es fácil. Llega con utilizar la opción “Flip U/D” (flip upside down) en el editor de sprites.

Para los demás “frames” habrá que trabajárselo un poco más o usar algún programa de utilidad. Yo he usado la opción “Rotate” del editor de sprites, que se alimenta con el ángulo de rotación (45 grados, 90 grados, etc.). Lo que ocurre es que esta opción funciona bien con sprites monocolor y, en el caso de sprites multicolor, hay que repasar el resultado a fondo.

Al final tendremos ocho “frames” con aspectos similares estos:

Asteroids - Frames

Es más, utilizando la opción “Animation” del editor de sprites se puede ver el efecto que hará la nave al girar:

Asteroids - Giro 2

Por último, queda exportar los datos de los “frames” a un archivo binario. Esto se consigue con la opción File > Export > To Binary File. Le indicamos que exporte todos los sprites o frames, que rellene a 64 octetos (recordemos que un sprite en realidad sólo ocupa 63 bytes), y llamamos al fichero binario “Sprites.bin” o similar.

Posteriormente incluiremos el fichero “Sprites.bin” en el juego mediante la directiva “incbin Sprites.bin”, que está en el fichero “Sprites.asm”. Más adelante, cuando tengamos más cosas que incluir, este fichero se convertirá en “Recursos.asm”.

Asteroids - Inc sprites jugador


Código del proyecto: Asteroids01

Asteroids: la pantalla de juego

Tengo entendido que el espacio se ve negro, ¿no? Pues hale, allá vamos: borde negro y fondo negro.

Además, conviene borrar la pantalla (pintar espacios) para que no salgan las letras previas relativas a la carga del juego, así como configurar la RAM de color a blanco en previsión de que en el futuro queramos añadir texto. Negro sobre negro se vería muy mal.

Todo esto podemos resolverlo con la librería «LibChars»:

  • Color de borde y color de fondo: Con la rutina “configuraColores”, que hay que empezar por mejorar para que admita también el color del borde (EXTCOL = $d020).
  • Relleno de la pantalla con espacios: Con la rutina “rellenaPantalla”.
  • Relleno de la RAM de color con blanco: Con la rutina “rellenaColor”.

Todo esto habrá que ejecutarlo en la inicialización, puesto que se ejecuta una vez y nada más. Al menos de momento; más adelante, cuando permitamos varias partidas sucesivas, habrá que darle una vuelta.

Más sencillo imposible. Desde aquí se llama a la rutina que inicializa la pantalla:

Asteroids - Pantalla de juego - Llamada

Y este es el contenido de la rutina:

Asteroids - Pantalla de juego

De momento, lo hemos ubicado todo en «Asteroids.asm», pero si más adelante se complica la gestión de la pantalla, y aparecen más rutinas relacionadas, perfectamente podemos llevar la rutina “inicializaPantalla” y todo lo que surja a un nuevo fichero «Pantalla.asm».


Código del proyecto: Asteroids01

Asteroids: estructura del proyecto

Antes de meternos en harina deberíamos pensar un poco cómo vamos a estructurar el proyecto. El proyecto es la forma que tiene CBM prg Studio de organizar los ficheros en ensamblador y otros ficheros asociados (ej. sprites, pantallas, etc.).

Es más, mejor que un único proyecto podemos utilizar varios. Como vamos a ir haciendo refinamientos sucesivos, mejor que trabajar sobre un único proyecto, podemos definir un proyecto, trabajarlo, probarlo y, cuando estén cumplidos sus objetivos, copiarlo como punto de partida para el siguiente proyecto. Así tendremos Asteroids01, Asteroids02, Asteroids03, etc. De este modo será fácil volver a un paso anterior e identificar los avances.

Todos y cada uno de los proyectos (carpetas con un fichero *.cbmprj) tendrán la siguiente estructura:

  • Un fichero «Asteroids.asm». Será el fichero principal, el punto de partida.
  • Varios ficheros «ConstXX.asm» y «LibXX.asm», correspondientes a las librerías que utilicemos: sprites, joystick, manejo de caracteres, etc.
  • Varios ficheros «XX.asm», por ejemplo, «Jugador.asm», «Disparos.asm», «Asteroides.asm», etc. Esto no es más que una forma de organizar el código en bloques autocontenidos, evitando tenerlo todo junto en un único fichero, lo que dificultaría mucho su comprensión.
  • Varios ficheros de recursos, por ejemplo, definiciones de sprites («Sprites.spt»), definiciones de caracteres personalizados («Caracteres.cst»), definiciones de pantallas («Pantallas.sdd»), etc.
  • El fichero «Mem.asm» con el mapa de memoria.

En resumen, algo así para empezar, aunque lo iremos complicando poco a poco:

Asteroids - Estructura inicial

Por su parte, el fichero principal, «Asteroids.asm», tendrá tres partes principales:

  • Un cargador BASIC. Esto es simplemente para facilitar la ejecución del programa tras su carga.
  • Una parte de inicialización. Esta parte incluirá el código preparatorio que sólo se tendrá que ejecutar una vez o, al menos, una vez por partida.
  • Una parte de bucle de juego. Típicamente todos los juegos tienen un bucle, en el que de forma sucesiva se van actualizando los diferentes elementos (la nave, los disparos, los asteroides, …) hasta que ocurre algo que nos saca del bucle, por ejemplo, que se han agotado las vidas.

Asteroids - Estructura principal

Y lo anterior ya nos hace reparar una cosa: los juegos del C64 no se cargaban, se jugaba una vez, y listo. Se cargaban y se jugaban muchas veces, hasta que el jugador o jugadores se cansaban, en cuyo caso seleccionaban una opción de volver a BASIC (raro) o más bien apagaban y volvían a encender. Por tanto, podemos empezar pensando directamente en la fase de juego, que es lo que sale natural, pero antes o después habrá que meter algo más que permita esa repetición de partidas.

Un primer juego sencillo: Asteroids

El primer juego debería ser sencillo, pero a la vez debería tener algunos ingredientes fundamentales del C64. Al menos:

Otros ingredientes más sofisticados, como pantallas, scroll o bitmaps, los dejamos para más adelante.

Podemos elegir el que más rabia nos dé. A mí me gustan los clásicos. Y uno de esos juegos clásicos es “Asteroids”. Seguro que lo conocéis. Si me hubieran preguntado en los 80 no me hubiera visto capaz de programarlo.

Asteroids - Clásico

El juego aparece descrito aquí: https://es.wikipedia.org/wiki/Asteroids. La idea básica es bien sencilla:

Tenemos una nave que se mueve por el espacio. Se encuentra con asteroides que, caso de colisionar con la nave, la destruyen. Pero la nave puede disparar a los asteroides y destruirlos, lo cual además reporta puntos al jugador. Según avanza el juego y se ganan puntos, cada vez hay más asteroides y se mueven más rápido. El objetivo es ganar el mayor número de puntos, puesto que es imposible salir del cinturón de asteroides ni destruirlos todos.

El juego clásico tiene algunas complicaciones adicionales, como un botón de “ciberespacio” para esquivar colisiones inminentes, que los asteroides destruidos dan lugar a otros asteroides más pequeños, y platillos volantes que te atacan. Pero la esencia del juego es la ya descrita. Y en todo caso esas complicaciones tampoco parecen difíciles de añadir.

¿Qué os parece como primer objetivo?

Lo dicho: vamos a por ello paso a paso…

Las herramientas

Las herramientas, en principio, son las ya conocidas:

Pero, nuevamente, siéntete libre para experimentar. Cambia VICE y CBM prg Studio por tu emulador y tu IDE favoritos. Cambia las librerías por otras que tengas por ahí o unas de desarrollo propio.

Las herramientas son sólo un apoyo. El material fundamental con el que vamos a trabajar ni siquiera son los programas: son las ideas.

Respecto a las librerías, es fácil que encontremos errores y/o mejoras. Las abordaremos sobre la marcha.

El proceso: paso a paso

El proceso es importante. Si presentamos un juego de golpe, por sencillo que éste sea, será muy difícil de digerir. Y más en ensamblador.

Por tanto, como el objetivo es explicarse, lo mejor es desarrollar y presentar los juegos paso a paso. Cada paso será una pequeña elaboración del paso anterior, de modo que sea fácil construir y crecer.

Esta no es sólo la mejor manera de explicarse. Además, me parece la mejor manera de desarrollar en general. Si se intenta abordar el proyecto de golpe, como un todo sin sus partes, es probable que se fracase. No en vano, las metodologías ágiles están muy de moda desde hace unos años.

A lo largo del camino cometeremos errores. Y tendremos que corregir, cambiar el rumbo, hacer rediseños, etc. Mejor. De pocas cosas se aprende tanto como de los errores. Y si los errores son propios se aprende más todavía. Los éxitos se olvidan pronto; los errores perduran en la memoria…

Y así, paso a paso, hasta que completemos cada proyecto. Y es muy aconsejable experimentar: cambiar el diseño, recodificar partes, hacer mejoras, comentar en el blog, etc.

Continuamos: programación de juegos

Con el blog y el libro “Programación Retro del Commodore 64” hemos aprendido:

  • Herramientas modernas para trabajar con el C64 (VICE y CBM prg Studio).
  • Los sistemas de numeración decimal, hexadecimal y binario.
  • El hardware del C64.
  • El mapa de memoria.
  • La programación en ensamblador del 6510.
  • Las rutinas del Kernal.
  • La programación de gráficos con el VIC.
  • La programación de música y sonido con el SID.
  • La programación de entrada/salida con los CIAs.

Todo ello con muchos ejemplos de código en ensamblador descargables del blog.

Pues bien, la continuación natural de este trabajo sería desarrollar uno o varios proyectos en ensamblador. Y, lógicamente, cuando hablamos de “proyectos” para el C64 esto viene a ser sinónimo de “juegos”, aunque Commodore vendía el C64 como una máquina de propósito general para el hogar.

Así pues, nos disponemos a desarrollar uno o varios juegos en ensamblador. Hasta que el cuerpo aguante. No sé si llegaremos a uno, dos, tres, o los que sean. Lógicamente, deberían tener enfoques diferentes y complejidades crecientes.

Y no sólo desarrollarlos; sobre todo explicarlos. Ese quiero que sea el elemento diferenciador: explicar cómo se desarrollan y cómo funcionan.

Por tanto, el objetivo no va a ser desarrollar el mejor juego. Ya hay muchísimos juegos para el C64 y no veo posible superar eso. Tampoco hacer unos gráficos o una música estupendos; no creo que lo consiguiera. Ni migrar aquel juego de Spectrum que me encantaba y que nunca llego a salir para Commodore.

El objetivo será triple: explicar, explicar y explicar.

Por ello, cuando tengamos que elegir entre optimización o claridad, salvo que no quede otra alternativa, la cuestión está zanjada: claridad.

A ello vamos…

¡¡Me he comprado un C64C!!

Después de darle muchas vuelas, y dudarlo mucho, por fin lo he hecho: ¡¡me he comprado un C64C!! Lo he comprado en eBay y me llegará en unos 15 días.

Adjunto algunas fotos para que veáis lo chulo que es:

C64C comprado

C64C comprado 2

A mí el modelo C64C me gusta más que el modelo «panera» original. Es básicamente el mismo equipo, pero el diseño exterior me gusta más.

Tiene caja original, fuente de alimentación original, y todo lo demás. Sólo tendré que añadirle un cable de vídeo como éste y un SD2IEC para guardar el software.

Aprovechando que hablamos de compras, voy a comentaros algunas tiendas retro que me gustan. Casi todas son tiendas de eBay.

La primera tienda es commodore_retro_store, que es de Manchester (Reino Unido). Suele tener bastantes equipos Commodore, muchos tuneados (carcasas especiales, Jiffydos, Lumafix, etc.), y también complementos. Además ofrecen servicio de reparaciones, y admiten devoluciones.

Otra tienda es 8bit_retro_shop, que es de Alemania, y también tiene bastante variedad de equipos y complementos, aunque hasta la fecha no he visto equipos tuneados.

Por último, también conozco la tienda manuelspeg0, también de Alemania, que suele tener equipos chulos y muy bien cuidados. En algún caso me han tratado regular al pedir información sobre alguno…

En el terreno nacional, una tienda que me gusta mucho es Retrocables, que es una tienda de Leganés que vende cables y complementos para equipos retro. No hace falta decir lo útil que resulta esto para cualquier aficionado al retro.

Retrocables

Su sección para C64 es muy interesante. Ahí se pueden encontrar cables de vídeo, fuentes de alimentación, cintas de cassette, joysticks, chips SID, etc.

Por último, aprovecho para decir que espero continuar en breve con el blog. La idea es desarrollar uno o varios juegos con enfoques y complejidades distintas y, sobre todo, explicar cómo se desarrollan y cómo funcionan.