lunes, 18 de abril de 2016

Cómo escribir juegos para el ZX Spectrum. Capítulo 4

Índice de entradas

Esta serie de artículos han sido traducidos a partir del documento "How to Write ZX Spectrum Games" con permiso de su autor, Jonathan Cauldwell, un gran desarrollador de juegos para el Spectrum, os recomiendo visitar su Web donde está el texto original. El documento original, y por tanto esta traducción, tiene © Jonathan Cauldwell y solo puede duplicarse con permiso expreso por escrito de su autor.
 
ACTUALIZADO 21/09/2022: Se añaden al final los fuentes, ahora ejecutables según el nuevo apéndice C

Números aleatorios

La generación de números aleatorios en código de máquina puede ser un problema difícil para un programador novato.

En primer lugar vamos a dejar una cosa clara, no hay una cosa que sea un generador de números aleatorios. La CPU se limita a seguir las instrucciones y no tiene mente propia, no puede simplemente sacar un número de la nada sobre la base de un capricho. En su lugar, tiene que seguir una fórmula que producirá una secuencia impredecible de números que no parecen seguir ningún tipo de patrón, y por lo tanto dan la impresión de aleatoriedad. Todo lo que podemos hacer es devolver un falso (o seudo) número aleatorio.

Un método de obtención de un número seudo-aleatorio sería el uso de la secuencia de Fibonacci, sin embargo el método más fácil y más rápido de generar un número de 8 bits seudo-aleatorio en el Spectrum es poner un puntero hacia la ROM, y examinar el contenidos del byte en ese lugar a su vez. Hay un pequeño inconveniente en este método, la ROM de Sinclair contiene una área muy uniforme y poco aleatoria hacia el final que es mejor evitar. Al limitar el puntero a, por ejemplo, los primeros 8K ROM tenemos una secuencia de 8192 números de "al azar", más que suficiente para la mayoría de los juegos. De hecho, todos los juegos que he escrito con un generador de números aleatorios utilizan este método, o uno muy similar:

; Sencillo generador de números seudo-aleatorio.
; Seguir un puntero a través de la ROM (a partir de una semilla),
; retornando el contenido del byte en esa posición.

random ld hl,(seed)        ; puntero
       ld a,h
       and 31              ; mantenerlo en los primeros 8Kb de ROM.
       ld h,a
       ld a,(hl)           ; Obtener el número "aleatorio" de esa ubicación.
       inc hl              ; Incrementar el puntero.
       ld (seed),hl
       ret
seed   defw 0

Vamos a poner nuestro nuevo generador de números aleatorios para utilizarlo en nuestro juego Centipede. Todos los juegos centipede necesita setas - muchas- dispersas al azar en todo el área del juego, y ahora podemos llamar a la rutina aleatoria para que nos suministre las coordenadas donde se muestra cada seta. Las partes resaltadas son los que tenemos que añadir.

; Queremos una pantalla en negro.

       ld a,71             ; tinta blanca (7) en fondo negro (0),
                           ; con brillo (64).
       ld (23693),a        ; establecer nuestros colores de pantalla.
       xor a               ; forma rápida de cargar el acumulador con cero.
       call 8859           ; establecer el colore del borde permanente.

; Configurar los gráficos.

       ld hl,blocks        ; dirección de los datos para los UDG.
       ld (23675),hl       ; apuntar los UDG hacia aquí.

; De acuerdo, vamos a empezar el juego.

       call 3503           ; rutina ROM - borra la pantalla, abre el canal 2.

; Inicializar coordenadas.

       ld hl,21+15*256     ; cargar el par hl con las coordenadas iniciales.
       ld (plx),hl         ; fijar las coordenadas del jugador.

       call basexy         ; establecer las posiciones x e y del jugador.
       call splayr         ; mostrar símbolo de la base del jugador.
       
; Ahora queremos llenar la zona de juegos con setas.

       ld a,68             ; tinta verde (4) en fondo negro (0),
                           ; con brillo (64).
       ld (23695),a        ; establecer nuestros colores temporales.
       ld b,50             ; comenzar con unas pocas.
mushlp ld a,22             ; código del carácter de control para AT.
       rst 16
       call random         ; obtener un número 'aleatorio'.
       and 15              ; en vertical en rango de 0 a 15.
       rst 16
       call random         ; obtener otro número seudo-aleatorio.
       and 31              ; horizontal en el rango de 0 a 31.
       rst 16
       ld a,145            ; el UDG 'B' es el gráfico de las setas.
       rst 16              ; poner la seta en la pantalla.
       djnz mushlp         ; bucle hasta que todas las setas aparezcan.

; Este es el bucle principal.

mloop  equ $

; Borrar el jugados.

       call basexy         ; establecer las posiciones x e y del jugador.
       call wspace         ; mostrar un espacio sobre el jugador.

; Ahora hemos eliminado el jugador y lo movemos antes de volverlo a mostrar
; en sus nuevas coordenadas.

       ld bc,63486         ; fila del teclado 1-5/joystick puerto 2.
       in a,(c)            ; ver que teclas están pulsadas.
       rra                 ; bit mas externo = tecla 1.
       push af             ; recordar el valor.
       call nc,mpl         ; si está siendo pulsada, moverse a la izquierda.
       pop af              ; restaurar el acumulador.
       rra                 ; siguiente bit (valor 2) = tecla 2.
       push af             ; recordar el valor.
       call nc,mpr         ; si está siendo pulsada, moverse a la derecha.
       pop af              ; restaurar el acumulador.
       rra                 ; siguiente bit (valor 4) = tecla 3.
       push af             ; recordar el valor.
       call nc,mpd         ; si está está siendo pulsada, moverse hacia abajo.
       pop af              ; restaurar el acumulador.
       rra                 ; siguiente bit (valor 8) = tecla 4.
       call nc,mpu         ; si está está siendo pulsada, moverse hacia arriba.

; Ahora que se ha movido podemos volver a mostrar al jugador.

       call basexy         ; establecer las posiciones x e y del jugador.
       call splayr         ; mostrar al jugador.

       halt                ; retardo.

; Saltar de nuevo al principio del bucle principal.

       jp mloop

; Mover al jugador a la izquierda.

mpl    ld hl,ply           ; recordar, ¡y es la coordenada horizontal!
       ld a,(hl)           ; ¿cuál es el valor actual?
       and a               ; ¿es cero?
       ret z               ; sí - no podemos seguir hacia la izquierda.
       dec (hl)            ; restar 1 a la coordenada y.
       ret

; Mover al jugador a la derecha.

mpr    ld hl,ply           ; recordar, ¡y es la coordenada horizontal!
       ld a,(hl)           ; ¿cuál es el valor actual?
       cp 31               ; ¿está en el borde derecho (31)?
       ret z               ; sí - no podemos seguir hacia la derecha.
       inc (hl)            ; sumar 1 a la coordenada y.
       ret

; Mover al jugador hacia arriba.

mpu    ld hl,plx           ; recordar, ¡x es la coordenada vertical!
       ld a,(hl)           ; ¿cuál es el valor actual?
       cp 4                ; ¿está en el límite superior (4)?
       ret z               ; sí - no podemos seguir hacia arriba.
       dec (hl)            ; restar 1 a la coordenada x.
       ret

; Mover al jugador hacia abajo.

mpd    ld hl,plx           ; recordar, ¡x es la coordenada vertical!
       ld a,(hl)           ; ¿cuál es el valor actual?
       cp 21               ; ¿está en el límite inferior (21)?
       ret z               ; sí - no podemos seguir hacia abajo.
       inc (hl)            ; sumar 1 a la coordenada x.
       ret

; Configurar las coordenadas X e Y de la posición de la base del jugador,
; se le llama antes de la visualización y supresión de la base.

basexy ld a,22             ; código para AT.
       rst 16
       ld a,(plx)          ; coordenada vertical del jugador.
       rst 16              ; fijar la posición vertical del jugador.
       ld a,(ply)          ; coordenada horizontal del jugador.
       rst 16              ; fijar la posición horizontal del jugador.
       ret

; Mostrar al jugador en la posición de impresión actual.

splayr ld a,69             ; tinta cían (5) en fondo negro (0),
                           ; brillante (64).
       ld (23695),a        ; establecer nuestros colores temporales de pantalla.
       ld a,144            ; código ASCII para el UDG 'A'.
       rst 16              ; dibujar jugador.
       ret

wspace ld a,71             ; tinta blanca (7) en fondo negro (0),
                           ; brillante (64).
       ld (23695),a        ; establecer nuestros colores temporales de pantalla.
       ld a,32             ; carácter de ESPACIO.
       rst 16              ; dibujar espacio.
       ret

; Sencillo generador de números seudo-aleatorio.
; Seguir un puntero a través de la ROM (a partir de una semilla),
; retornando el contenido del byte en esa posición.

random ld hl,(seed)        ; puntero
       ld a,h
       and 31              ; mantenerlo en los primeros 8Kb de ROM.
       ld h,a
       ld a,(hl)           ; Obtener el número "aleatorio" de esa ubicación.
       inc hl              ; Incrementar el puntero.
       ld (seed),hl
       ret
seed   defw 0 

plx    defb 0              ; coordenada x del jugador.
ply    defb 0              ; coordenada y del jugador.

; gráficos UDG.

blocks defb 16,16,56,56,124,124,254,254    ; base del jugador.
       defb 24,126,255,255,60,60,60,60     ; seta.


Una vez ejecutado este listado se parece más a un juego de Centipede de lo que lo hacía antes, pero hay un problema importante. Las setas se distribuyen de forma aleatoria alrededor de la pantalla, pero el jugador puede moverse a través de ellas. Se requiere algún tipo de detección de colisiones para evitar que esto ocurra, lo que se cubrirá en el siguiente capítulo.

3 comentarios:

  1. Muchas gracias por el esfuerzo!
    Excelente trabajo de traduccion!!!
    Enhorabuena y seguid asi por favor!

    ResponderEliminar
    Respuestas
    1. De nada, las traducciones las hago yo solo, no es trabajo de equipo, y las reviso poco por lo que seguro son mejorables.

      Eliminar
  2. Aun mas merito tiene si las haces solo!
    En ese caso, rectifico:
    Enhorabuena, adelante y sigue asi , que estas haciendo un excelente trabajo!
    Saludos!

    ResponderEliminar