martes, 19 de abril de 2016

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

Í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.

Detección sencilla de colisiones con el fondo

Encontrando atributos

Cualquiera que haya pasado tiempo programando en Sinclair BASIC puede recordar la función ATTR. Es una manera de detectar los atributos de color de cualquier celda de carácter de la pantalla, y aunque es complicado de captar para el programador en BASIC, es muy útil para la detección sencilla de colisiones. El método era tan útil, de hecho, que su equivalente en lenguaje  máquina fue empleado en una serie de juegos comerciales, y es de gran utilidad para el programador novato del Spectrum.

Hay dos maneras de encontrar los parámetros de los atributos de color de una celda de carácter particular en el Spectrum. Una mirada rápida al desensamblado de la ROM del Spectrum revela una rutina en la dirección 9603 que va a hacer el trabajo por nosotros, o también podemos calcular la dirección de memoria por nosotros mismos.

La forma más sencilla de encontrar un valor de atributo es usar un par de rutinas de la ROM:

       ld bc,(ballx)       ; poner x,y en el par de registros bc.
       call 9603           ; llamada ROM, obtiene el atributo (c,b) en la pila.
       call 11733          ; poner los atributos en el acumulador.

Sin embargo, es mucho más rápido hacer el cálculo nosotros mismos. También es útil calcular la dirección de un atributo y no sólo su valor en caso de que también queramos escribirlo.

Calculando Direcciones de Atributos

A diferencia del torpe diseño de pixeles del Spectrum (NdT: de esto se habla mas adelante, pero solo hay que ver como se van presentando las pantallas de carga de los juegos para hacerse una idea), las céldas de color, ubicadas en las direcciones 22.528 a 23.295 ambas inclusive, están dispuestas secuencialmente en la memoria RAM tal y como cabría esperar. En otras palabras, las céldas de atributo de la primera línea de la pantalla se encuentran en las direcciones 22528 a 22559 y van de izquierda a derecha, la segunda fila de celdas de colores van desde 22560 a 22591, y así sucesivamente. Para encontrar la dirección de una celda de color en la posición de impresión (x, y) sólo tenemos que multiplicar x por 32, sumarle y, finalizando con añadir 22528 al resultado. Al examinar el contenido de esta dirección podemos encontrar los colores que se muestran en una posición particular y actuar en consecuencia. En el siguiente ejemplo se calcula la dirección de un atributo en la posición de carácter (b, c) y lo devuelve en el par de registro HL.

; Calcular la dirección del atributo de carácter en (b,c).

atadd  ld a,b              ; posición x.
       rrca                ; multiplicar por 32.
       rrca
       rrca
       ld l,a              ; guardar también en l.
       and 3               ; enmascarar bits para byte alto.
       add a,88            ; 88*256=22528, inicio de atributos.
       ld h,a              ; byte alto completado.
       ld a,l              ; obtener x*32 de nuevo.
       and 224             ; enmascarar el byte bajo.
       ld l,a              ; guardar en l.
       ld a,c              ; obtener desplazamiento de y.
       add a,l             ; añadir al byte bajo.
       ld l,a              ; hl=Dirección de atributos.
       ld a,(hl)           ; devolver atributo en a.
       ret

Al mirar el contenido del byte en HL nos dará el valor del atributo, mientras que la escritura en la posición de memoria apuntada por HL cambiará el color en ese lugar.

Para dar sentido a los resultados, tenemos que saber que cada atributo se compone de 8 bits que están dispuestos de esta manera:

d0-d2  color de la tinta 0-7, 0=negro, 1=azul, 2=rojo,     3=magenta,
                              4=verde, 5=cian, 6=amarillo, 7=blanco
d3-d5  color del fondo 0-7,   0=negro, 1=azul, 2=rojo,     3=magenta,
                              4=verde, 5=cian, 6=amarillo, 7=blanco
d6     brillo,                0=normal, 1=alto
d7     intermitencia,         0=no, 1=intermitente

Mirar si el fondo es verde por ejemplo, podría implicar

       and 56              ; enmascarar todos los bits menos el fondo.
       cp 32               ; ¿es verde (4) * 8?
       jr z,green          ; Sí, hacer lo que sea cuando es verde.

mientras que la comprobación de si la tinta es color amarillo se podría hacer de esta manera

       and 7               ; sólo queremos los bits de la tinta.
       cp 6                ; ¿es de color amarillo (6)?
       jr z,yellow         ; Sí, hacer lo que sea cuando es amarillo .

Aplicando lo que hemos aprendido al Juego

Ahora podemos añadir una prueba de colisión por atributo a nuestro juego del ciempiés. Como antes, las nuevas secciones están remarcadas.

; 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á siendo pulsada, moverse hacia abajo.
       pop af              ; restaurar el acumulador.
       rra                 ; siguiente bit (valor 8) = tecla 4.
       call nc,mpu         ; si 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.

; antes comprobar que no hay una seta en el camino.

       ld bc,(plx)         ; coordenadas actuales.
       dec b               ; mirar una posición a la izquierda.
       call atadd          ; obtener la dirección del atributo en esta posición.
       cp 68               ; las setas son brillo (64) + verde (4).
       ret z               ; hay una seta, no podemos movernos aquí.

       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.

; antes comprobar que no hay una seta en el camino.

       ld bc,(plx)         ; coordenadas actuales.
       inc b               ; mirar una posición a la derecha.
       call atadd          ; obtener la dirección del atributo en esta posición.
       cp 68               ; las setas son brillo (64) + verde (4).
       ret z               ; hay una seta, no podemos movernos aquí.

       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.

; antes comprobar que no hay una seta en el camino.

       ld bc,(plx)         ; coordenadas actuales.
       dec c               ; mirar una posición hacia arriba.
       call atadd          ; obtener la dirección del atributo en esta posición.
       cp 68               ; las setas son brillo (64) + verde (4).
       ret z               ; hay una seta, no podemos movernos aquí.

       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.

; antes comprobar que no hay una seta en el camino.

       ld bc,(plx)         ; coordenadas actuales.
       inc c               ; mirar una posición hacia abajo.
       call atadd          ; obtener la dirección del atributo en esta posición.
       cp 68               ; las setas son brillo (64) + verde (4).
       ret z               ; hay una seta, no podemos movernos aquí.

       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

; Calcular la dirección del atributo de carácter en (dispx, dispy).

atadd  ld a,c              ; coordenada vertical.
       rrca                ; multiplicar por 32.
       rrca                ; desplazar a la derecha con acarreo 3 veces
       rrca                ; mas rápido que desplazar izquierda 5 veces.
       ld e,a
       and 3
       add a,88            ; 88x256=dirección de los atributos.
       ld d,a
       ld a,e
       and 224
       ld e,a
       ld a,b              ; posición horizontal.
       add a,e
       ld e,a              ; de=dirección de los atributos.
       ld a,(de)           ; devolver atributo en el acumulador.
       ret

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.


NdT: Los cuatro bloques de código para detectar las colisiones son iguales salvo una instrucción, recomiendo reemplazarlos por una MACRO en lugar de repetir código, podía ser por ejemplo esto (la sintaxis exacta depende de su programa ensablador, este es el ejemplo en PASMO):

MACRO  COLISION, INS,REG
       ld bc,(plx)         ; coordenadas actuales.
       INS REG             ; mirar una posición a la izquierda.
       call atadd          ; obtener la dirección del atributo en esta posición.
       cp 68               ; las setas son brillo (64) + verde (4).
       ret z               ; hay una seta, no podemos movernos aquí.
       
       INS (hl)            ; sumar o restar 1 a la coordenada
       ret
ENDM

......

; antes comprobar que no hay una seta en el camino.

       COLISION, dec, c

No hay comentarios:

Publicar un comentario