Programación #1: Motor de plataformas BASIC Spectrum

aphix.jpgComo dije ayer (por cierto, bajada histórica de visitas con “sólo” 135, ¿where are you? :D), voy a intentar tematizar un poco el blog por días de la semana. Los martes me dedicaré a la programación. Os hablaré de programación para micros retro o para PC en diversos lenguajes, según el tiempo que tenga para escribir (esto siempre lo hago en tiempo real, no tengo tiempo para prepararme el artículo y luego postearlo en condiciones) y lo que me apetezca.

Para empezar, y a colación del juego de ayer (Phantomasa), os voy a explicar cómo se hace un motor de plataformas sencillo como el que lleva este juego, programado en BASIC Spectrum y preparado para compilar con HiSoft BASIC. Todos los archivos que necesitemos los pongo siempre abajo en el ZIP total, así que no preocuparos que no os faltará de nada.

Como siempre, uno no sabe muy bien cómo está la gente de nivel, así que me he ido de un poco generalista. Si sabes algo de BASIC mejor, vamos, o si no ¿qué coño esperas? ¡que es muy fácil, parguelilla de pacotilla! ¡Buscador de entretelas! ¡Arremetedor de cojines! ¡Aprende BASIC ya, carajo!  Y pásatelo guay.


La mayoría de los tutoriales son un montón de aburridos, y te hacen los ejemplos en plan “teórico“. Nosotros no, así que lo primero que vamos a hacer es conseguirnos unos UDGs para nuestro muñeco. Como me gustaba mucho como salía en Microhobby, me copio vilmente:

notas_graficas.png

Si nos fijamos con atención, veremos que los carácteres AB/CD y AB/EF nos servirán para dibujar los dos frames de animación de nuestro sprite mirando a la izquierda, y que GH/IJ y GH/KL nos servirán para lo mismo, pero mirando a la derecha. Los UDGs de la M a la U están ahí para dibujarse fondos, pero en eso no entraremos hoy.

Estos UDGs los he metido en el paquetito con el nombre de udg.bin y se pueden cargar símplemente arrastrando el archivo a la ventana de Spectaculator (bajaros la versión 5.3 de la web de Ferry, es gratis) y poniendo la dirección 65368, que es donde empiezan los UDGs en RAM.

Una vez tenemos los gráficos, os voy a detallar un poco en qué se basa el algoritmo, cómo funciona y qué pinta tiene, y luego iremos construyendo, poco a poco (bueno, en realidad son 5 o 6 lineas) el código supermegahiperrápido.

Veamos. Si alguna vez has hecho un juego lo sabrás, y si no lo vas a saber ahora: todos los juegos se basan en lo que se vino a llamar el game loop, o sea, el bucle de juego. Un loop o bucle, en el ámbito de la programación, es algo que se repite una y otra y otra vez mientras no venga dios y diga lo contrario (aquí dios eres tú, querido programador).

En el bucle de juego se realizan siempre las mismas operaciones: mover los sprites, comprobar cosas, y mostrar gráficos en la pantalla. Para ahorrar tiempo y por la peculiaridad del BASIC, los gráficos se muestran al moverlos. Por lo general (por ejemplo, programando en C+Allegro para plataformas actuales) se suele mover todo y luego pintar todo, de forma separada, por claridad y facilidad de depuración y mil cosas más. Pero en Sinclair BASIC queremos velocidad a bajo coste.

Nosotros vamos a crear una subrutina que mueva al personaje principal dependiendo de las teclas que pulsemos, y que además lo dibuje en pantalla. Será una de las subrutinas que hay que llamar (con GOSUB) dentro de nuestro bucle de juego. Más o menos algo así, en esquemático vacío:

10 GOTO 1000
100 REM subrutina de movimiento
110 REM cosas
200 RETURN
1000 REM game loop
1010 GOSUB 100: REM movimiento
1020 GOTO 1000

Ea. Listo. Ahora tenemos que rellenar esto para que haga algo, pero básicamente esa es la estructura de nuestro programa. Teniendo claro esto, vamos a explicar en qué se basa la rutina de movimiento de plataformas que os voy a explicar acto seguido.

Como sabemos, la pantalla del Spectrum se divide en celdas de carácter y cada una de ellas puede tener un atributo de color. Pues bien, nos basaremos en ese atributo de color para decidir si hay o no un obstáculo que deba detener el movimiento de nuestro personaje. Haremos que sólo podamos avanzar si todas las casillas que vamos a ocupar son PAPER 0, INK 7, BRIGHT 0, FLASH 0. Este valor, en ATTRibutos, es 7 + 8*0 + 64*0 + 128*0 = 7.

Sabido esto, os comento el algoritmo en pseudocódigo. Es algo así:

  1. ¿Salto activado? -> GOTO 3.
  2. Comprobar si se cae.
  3. Comprobar si se pulsa IZQUIERDA y mover si se puede.
  4. Comprobar si se pulsa DERECHA y mover si se puede.
  5. Comprobar si se pulsa SALTO para activarlo.
  6. Si SALTO desactivado -> RETURN
  7. Subir si se puede.
  8. RETURN

Veamos: si has jugado a Phantomasa 1, conocerás que el “salto” es en realidad una especie de “jetpac” que se activa al pulsar la tecla de salto y hace elevar al sprite durante un tiempo, tras lo cual empezamos a caer. Nosotros tendremos dos variables para manejar esto: una bandera que indica si el “salto” está activo y un contador que desactivará el salto al llegar a determinado valor (cuanto más alto sea este valor, más tiempo estaremos saltando).

Por tanto, si el salto está activo, debemos evitar que el sprite caiga. Eso es lo que se hace en la linea 1: si la bandera de salto está activa, saltamos a la línea 3, ya que en la 2 es en la que comprobaremos que no haya nada debajo del sprite para hacerlo caer.

En las lineas 3 y 4 miramos si pulsamos las teclas IZQUIERDA o DERECHA para mover a nuestro sprite (si es posible) en esas direcciones. En la linea 5, comprobamos si pulsamos la tecla de Salto. En esta linea, comprobaremos también que el sprite tenga un apoyo, y si es así, activaremos el salto y reiniciaremos su contador.

En la linea 6, si el salto esta desactivado, volvemos al bucle de juego. Si no, subimos, incrementamos el contador de salto, y volvemos.

Si no lo entiendes, estudia el código y verás como lo ves. Además, abajo hay un snapshot con el código funcionando para ahorrarte incluso teclearno. Joder, si es que soy un solete. He indentado un poco el código para intentar hacerlo más legible. Esperemos que quede clarito🙂

100 LET fr=1-fr:
    IF sal=1 THEN GOTO 120
110 IF y<20 THEN
       IF ATTR(y+2,x)=7 AND ATTR(y+2,x+1)=7 THEN
          PRINT AT y,x;"  ";AT y+1,x;a$;AT y+2,x;b$:
          LET y=y+1
120 IF INKEY$="o" THEN
       IF x>0 THEN
          IF ATTR(y,x-1)=7 AND ATTR(y+1,x-1)=7 THEN
             LET a$="AB":
             LET b$="CD":
             LET c$="EF":
             LET x=x-1:
             PRINT AT y,x;a$;" ";AT y+1,x;(b$ AND fr=0);(c$ AND fr=1);" "
130 IF INKEY$="p" THEN
       IF x<30 THEN
          IF ATTR(y,x+2)=7 AND ATTR(y+1,x+2)=7 THEN
             LET a$="GH":
             LET b$="IJ":
             LET c$="KL":
             PRINT AT y,x;" ";a$;AT y+1,x;" ";(b$ AND fr=0);(c$ AND fr=1);:
             LET x=x+1
140 IF INKEY$="q" THEN
       IF sal=0 THEN
          IF ATTR(y+2,x)<>7 OR ATTR(y+2,x+1)<>7 THEN
             LET sal=1:
             LET nu=0
150 IF sal=0 THEN
       RETURN
160 IF ATTR(y-1,x)=7 AND ATTR(y-1,x+1)=7 THEN
       LET y=y-1:
       PRINT AT y,x;a$;AT y+1,x;b$;AT y+2,x;"  "
170 LET nu=nu+1:
    IF nu=5 THEN
       LET sal=0
180 RETURN

Empleamos varias variables. A saber, tenemos x e y que nos indican la posición de nuestro sprite. Nuestro sprite ocupa 16×16 pixels, es decir, 4 celdas de carácter en un cuadrado de 2×2 carácteres. Por tanto, como lo vamos a dibujar con PRINT, tendremos la mitad superior en (y, x) y la inferior en (y + 1, x). La mitad superior no cambia, pero la inferior tiene dos frames que se van alternando. Eso lo controla la variable fr, que va tomando, alternativamente, los valores 0 y 1.

En las tres cadenas a$, b$ y c$ almacenamos los gráficos (mirando a izquierda o derecha) de nuestro sprite. en a$ irá la parte superior (la que se imprime en (y,x)) y en b$ y c$ los dos frames de animación de la parte inferior (la que se imprime en (y+1,x)).

Las variables sal y nu controlan el salto. La primera es la bandera de salto y la segunda un contador (se podría haber hecho con una sola variable pero así queda más clarito).

Voy a explicar brevemente qué hace cada linea de código (con algo más de detalle) y os dejo las descargas. Espero que esto os sirva como base para empezar a hacer algo. Uniendo esto con mis artículos en la revista ZXSF (donde explico como organizar las cosas para compilar) podréis crear juegos muy jugables con relativamente poco esfuerzo.

Línea 100

En esta línea lo primero que hacemos es “flip-flop” con fr. Si fr vale 0, pasará a valer 1 y si vale 1, pasará a valer 0. Luego comprobamos si el salto está activado. Si lo está, pasamos a la línea 120.

Línea 110

En esta línea comprobaremos si tenemos que hacer caer al sprite. Para ello, lo primero que miramos es que no nos vayamos a salir de la pantalla (y<20), y luego miramos los atributos que hay debajo del sprite, o sea, en las posiciones (y+2, x) y (y+2, x+1). Si ambos valen “7” significa que no hay ningún obstáculo y entonces imprimimos el sprite una posición más abajo (borrando con espacios el trozo que queda atrás) e incrementamos la variable y.

Línea 120

Esta línea es la que hace el movimiento hacia la izquierda. Primero comprueba que estemos pulsando la tecla “o“. Luego comprueba que no nos salgamos de la pantalla (x>0) y, finalmente, comprobamos, de nuevo con ATTR, que los atributos a la izquierda del sprite (en las posiciones (y, x-1) y (y+1, x-1)) valen 7. Si todo eso se cumple, decrementamos x, asignamos a a$, b$ y c$ los UDGs del sprite mirando hacia la izquierda, e imprimimos el sprite en la nueva posición pintando un espacio a su derecha para borrar el trozo que queda tras el desplazamiento. La parte con los ANDs nos garantiza que se imprimirá b$ o c$ dependiendo de si fr vale 0 ó 1.

Linea 130

Funciona de forma análoga a la 120, pero con el movimiento hacia la derecha. Seguimos los mismos pasos: que estemos pulsando “p“, que no nos salgamos de la pantalla (x<30), y que podamos avanzar (los atributos en las posiciones (y,x+2) y (y+1,x+2) valen 7). Entonces asignamos los valores correctos a a$, b$ y c$ con los UDGs del sprite mirando a la derecha, imprimimos un espacio y el gráfico (para borrar el trozo que queda tras el movimiento) y finalmente incrementamos x.

Linea 140

Esta linea controla la activación del salto. Si pulsamos “q“, comprobamos que bajo el sprite haya una plataforma (o sea, que en una de las posiciones bajo el sprite, de coordenadas (y+2,x) y (y+2,x+1) haya un atributo distinto de 7). En ese caso, activamos el salto (sal=1) e iniciamos el contador (nu=0).

Linea 150

Si el salto no está activo (sal=0), símplemente volvemos de la subrutina con RETURN.

Línea 160

Llegaremos a esta sección del código sólo si el salto está activo. Sí, amigo, lo has adivinado: esta es la sección del código que controla el salto. En esta linea veremos si no hay ningún obstáculo sobre el Sprite para hacerlo ascender (que los atributos en las posiciones (y-1,x) y (y-1,x+1) valen ambas 7). En ese caso decrementamos y, e imprimios el sprite en la nueva posición, borrando como siempre con espacios el trozo de sprite que queda tras el movimiento.

Línea 170

Esta línea controla la duración del salto. Se incrementa el contador nu y si llega a 5 se desactiva el salto (sal=0).

Linea 180

Volvemos de la subrutina con RETURN

prog01.png

Espero que haya quedado más o menos clarito. En el zip tienes un snapshot con todo funcionando, pintando un escenario chorra con el que interactuar. Para el próximo capítulo dedicado a la programación mejoraremos un poco este código añadiendo la posibilidad de pulsar varias teclas a la vez (INKEY$ sólo registra una pulsación al tiempo) usando lectura directa a los puertos de E/S que manejan el teclado.

Bajaros los tiestos de aquí. Un saludín.

8 Responses to Programación #1: Motor de plataformas BASIC Spectrum

  1. Siew dice:

    :O Tio, tu blog me gusta cada vez mas. Espero que la gente al ver este tipo de contenidos técnicos se anime a hacer sus propias cosas. ¿Cuando acabes con el BASIC seguiras con el z88dk?😉

  2. na_th_an dice:

    Sí, tengo varias cosas que poner que pueden ser de mucha ayuda, como por ejemplo explicaciones técnicas sobre la memoria dinámica de la splib2 que pueden ahorrar muchos dolores de cabeza.

    ¡Gracias por tu comentario!

  3. anjuel dice:

    Bueno, ahora no tenemos escusa para empezar a hacer cosas en basic.
    Si tengo dudas te masacraré a preguntas.
    Gracias maño!😀

  4. na_th_an dice:

    Pa eso está uno, pa que le masacren a preguntas🙂

  5. Mariano dice:

    Hola, me ha encantado el artículo, estoy interesandome por estas técnicas y el uso del Sprite en General y realmente lo has hecho sencillo en este práctico tutorial.

    Me encantaría que me pasaras en ZIP ya que no esta disponible de mediafire y me interesa bastante.

    Desde ya gracias y felicitaciones.

  6. jL dice:

    Vaya, se ha roto el enlace. La semana que viene que vuelvo de vacaciones lo arreglaré. Gracias por avisar🙂

  7. Montoliu dice:

    итак: благодарю!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: