jueves, 21 de abril de 2016

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

Í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 simple de colisiones con aliens

Comprobación de coordenadas

La comprobación de coordenadas deberían poder explicarla por sí mismo la mayoría de los programadores, pero se ha incluido aquí en aras de la exhaustividad. También es el siguiente paso en el desarrollo de nuestro juego del ciempiés.

El tipo más simple de detección de colisión sería algo como esto, que se utiliza para detectar si dos UDG han chocado:

       ld a,(playx)        ; coordenada x del jugador.
       cp (ix+1)           ; comparar con x del alien.
       ret nz              ; no es la misma, no hay colisión.
       ld a,(playy)        ; coordenada y del jugador.
       cp (ix+2)           ; comparar con y del alien.
       ret nz              ; no es la misma, no hay colisión.
       jp collis           ; tenemos una colisión.

Bueno, es bastante simple, pero la mayoría de los juegos no utilizan una sola celda de carácter para los gráficos. ¿Qué pasaría si los aliens son de cuatro celdas de caracteres de ancho por dos de alto, y el personaje del jugador es de tres celdas de alto por tres de ancho? Tenemos que comprobar si alguna parte del alien ha colisionado con cualquier parte del jugador, por lo que tenemos que comprobar que las coordenadas están dentro de un cierto rango. Si el alien está más cerca de dos cuadrados por encima del jugador o menos de 3 por debajo, entonces las coordenadas verticales coinciden. Si el alien está también a menos de cuatro celdas a la izquierda del jugador, y menos de tres celdas a la derecha, la posición horizontal también se comparte y tenemos una colisión.

Vamos a escribir algo de código para hacer esto. Podemos empezar por tomar la coordenada vertical del jugador:

       ld a,(playx)        ; coordenada x del jugador.

Luego restar la posición vertical del alien:

       sub (ix+1)          ; restar la x del alien.

A continuación, resta uno de la altura del jugador y sumárselo.

       add a,2             ; el jugador es de 3 de alto, luego añadir 3 - 1 = 2

Si el alien se encuentra dentro del rango, el resultado será menor que la altura combinada del jugador y el alien, por lo que realizamos la comprobación:

       cp 5                ; la altura combinada es 3 + 2 = 5.
       ret nc              ; no en el rango vertical.

De forma similar, podemos seguir este código para comprobar la horizontal:

       ld a,(playy)        ; coordenada y del jugador.
       sub (ix+2)          ; restar la y del alien.
       add a,2             ; el jugador es de 3 de ancho, luego añadir 3 - 1 = 2
       cp 7                ; el ancho combinado es 3 + 4 = 7.
       ret nc              ; no en el rango horizontal.
       jp collis           ; tenemos una colisión.

Por supuesto, este método no solo trabaja con gráficos basados en caracteres, también funciona perfectamente bien con los sprites, pero eso lo veremos más adelante. Es hora de FINALIZAR nuestro juego del Centipede con un poco de detección de colisiones. Como nuestros gráficos son todos UDG de un solo carácter no necesitamos mucha fantasía, una rápida verificación de si x = x y de si y = y es lo que necesitamos.
numseg equ 8               ; numero de segmentos del ciempiés.

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

       xor a               ; poner a cero el acumulador.
       ld (dead),a         ; poner el flag que indica que el jugador está vivo.

; Inicializar coordenadas.

       ld hl,21+15*256     ; cargar el par hl con las coordenadas iniciales.
       ld (plx),hl         ; fijar las coordenadas del jugador.
       ld hl,255+255*256   ; disparo del jugador por defecto.
       ld (pbx),hl         ; poner las coordenadas del disparo.

       ld b,10             ; número de segmentos a inicializar.
       ld hl,segmnt        ; tabla de segmentos.
segint ld (hl),1           ; comienza moviéndose a la derecha.
       inc hl
       ld (hl),0           ; comienza arriba.
       inc hl
       ld (hl),b           ; usa el registro B para la coordenada y.
       inc hl
       djnz segint         ; repetir hasta que todos se inicialicen.
       
       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.
       push af             ; recordar este valor.
       call nc,mpu         ; si está siendo pulsada, moverse hacia arriba.
       pop af              ; restaurar el acumulador.
       rra                 ; ultimo bit (valor 16) leer tecla 5.
       call nc,fire        ; se ha pulsado, moverse 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.

; Ahora para el disparo.  Primero vemos si ha golpeado algo.

       call bchk           ; ver la posición del disparo.

       call dbull          ; borrar disparo.
       call moveb          ; mover el disparo.
       call bchk           ; calcular nueva posición del disparo.
       call pbull          ; presentar el disparo en su nueva posición.

; Ahora los segmentos del ciempiés.

       ld ix,segmnt        ; tabla de datos del segmento.
       ld b,10             ; número de segmentos en la tabla.
censeg push bc
       ld a,(ix)           ; ¿está el segmento activado?
       inc a               ; 255=desactivado, incrementar a cero.
       call nz,proseg      ; si está activo, procesar el segmento.
       pop bc
       ld de,3             ; 3 bytes por segmento.
       add ix,de           ; poner el nuevo segmento en el registro ix.
       djnz censeg         ; repetir para todos los segmentos.
       
       halt                ; retardo.

       ld a,(dead)         ; ¿ha muerto el jugador por un segmento?
       and a
       ret nz              ; jugador muerto, perder una vida.

; 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

; Disparar un misil.

fire   ld a,(pbx)          ; coordenada vertical del proyectil.
       inc a               ; 255 es el valor por defecto, se incrementa a cero.
       ret nz              ; proyectil en pantalla, no puede volver a disparar.
       ld hl,(plx)         ; coordenadas del jugador.
       dec l               ; l esquina superior.
       ld (pbx),hl         ; establece las coordenadas del proyectil.
       ret

bchk   ld a,(pbx)          ; proyectil vertical.
       inc a               ; ¿Está a 255 (defecto)?
       ret z               ; si, no hay proyectil en la pantalla.
       ld bc,(pbx)         ; obtiene las coordenadas.
       call atadd          ; buscar el atributo de aquí.
       cp 68               ; seta es brillo (64) + verde (4).
       jr z,hmush          ; ¡Alcanzada una seta!
       ret

hmush  ld a,22             ; código de control para AT.
       rst 16
       ld a,(pbx)          ; proyectil vertical.
       rst 16
       ld a,(pby)          ; proyectil horizontal.
       rst 16
       call wspace         ; establecer el color de la tinta a blanco.
kilbul ld a,255            ; coordenada x a 255 = apagar el proyectil.
       ld (pbx),a          ; destruir el proyectil.
       ret

; Mover el proyectil arriba de la pantalla 1 carácter a la vez.

moveb  ld a,(pbx)          ; proyectil vertical.
       inc a               ; ¿Está a 255 (defecto)?
       ret z               ; si, no hay proyectil en la pantalla.
       sub 2               ; subir una línea.
       ld (pbx),a
       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
       
pbull  ld a,(pbx)          ; proyectil vertical.
       inc a               ; ¿Está a 255 (defecto)?
       ret z               ; si, no hay proyectil en la pantalla.
       call bullxy
       ld a,16             ; carácter de control para la tinta.
       rst 16
       ld a,6              ; 6 = amarillo.
       rst 16
       ld a,147            ; el UDG 'D' se usa para presentar el disparo.
       rst 16
       ret

dbull  ld a,(pbx)          ; proyectil vertical.
       inc a               ; ¿Está a 255 (defecto)?
       ret z               ; si, no hay proyectil en la pantalla.
       call bullxy         ; establece las coordenadas del proyectil.

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
       
; Establece las coordenadas x e y de la posición del  proyectil del jugador,
; se llama a esta rutina antes de presentar y borrar el proyectil.

bullxy ld a,22             ; código para AT.
       rst 16
       ld a,(pbx)          ; proyectil del jugador coordenada vertical.
       rst 16              ; establece la posición vertical del jugador.
       ld a,(pby)          ; posición horizontal del proyectil.
       rst 16              ; establece la coordenada horizontal.
       ret
       
segxy  ld a,22             ; código ASCII para el carácter de AT.
       rst 16              ; presentar código AT.
       ld a,(ix+1)         ; obtener la coordenada x del segmento.
       rst 16              ; posicionarse en esa coordenada.
       ld a,(ix+2)         ; obtener la coordenada y del segmento.
       rst 16              ; posicionarse en esa coordenada.
       ret

proseg call segcol         ; detección de colisión con un segmento.
       ld a,(ix)           ; verificar si el segmento está activo.
       inc a               ; para la rutina de detección de colisiones.
       ret z               ; está activo, por tanto pasa a muerto.
       call segxy          ; actualizar las coordenadas del segmento.
       call wspace         ; presentar un espacio, blanco sobre fondo negro.
       call segmov         ; mover el segmento.
       call segcol         ; mirar la colisión de nueva posición del segmento.
       ld a,(ix)           ; verificar si el segmento está activado.
       inc a               ; para la rutina de detección de colisiones.
       ret z               ; está activo, por tanto pasa a muerto.
       call segxy          ; actualizar las coordenadas del segmento.
       ld a,2              ; código de atributo = 2, segmento rojo.
       ld (23695),a        ; actualizar los atributos temporales.
       ld a,146            ; UDG 'C' para presentar el segmento.
       rst 16
       ret
segmov ld a,(ix+1)         ; coordenada x.
       ld c,a              ; área x GP.
       ld a,(ix+2)         ; coordenada y.
       ld b,a              ; área y GP.
       ld a,(ix)           ; indicador de estado.
       and a               ; ¿está el segmento en el borde izquierdo?
       jr z,segml          ; ir a la izquierda, saltar según ese bit de código.

; ¡ahora el segmento se mueve a la derecha!

segmr  ld a,(ix+2)         ; coordenada y.
       cp 31               ; ¿está en el borde derecho de la pantalla?
       jr z,segmd          ; si, mover el segmento hacia abajo.
       inc a               ; ver la izquierda.
       ld b,a              ; actualizar la coordenada y GP.
       call atadd          ; mirar el atributo de esa dirección.
       cp 68               ; setas son brillo (64) + verde (4).
       jr z,segmd          ; seta a la derecha, moverse hacia abajo.
       inc (ix+2)          ; sin obstáculos, seguir hacia la derecha.
       ret

; ¡ahora el segmento se mueve a la izquierda!

segml  ld a,(ix+2)         ; coordenada y.
       and a               ; ¿está en el borde izquierdo de la pantalla?
       jr z,segmd          ; si, mover el segmento hacia abajo.
       dec a               ; ver la derecha.
       ld b,a              ; actualizar la coordenada y GP.
       call atadd          ; mirar el atributo en la dirección (dispx,dispy).
       cp 68               ; setas son brillo (64) + verde (4).
       jr z,segmd          ; seta a la izquierda, moverse abajo.
       dec (ix+2)          ; sin obstáculos, seguir a la izquierda.
       ret

; ¡ahora el segmento se mueve hacia abajo!

segmd  ld a,(ix)           ; dirección del segmento.
       xor 1               ; cambiarla.
       ld (ix),a           ; guardar la nueva dirección.
       ld a,(ix+1)         ; coordenada y.
       cp 21               ; ¿llegamos al final de la pantalla?
       jr z,segmt          ; si, mover el segmento al inicio.


; En este momento nos estamos moviendo hacia abajo independientemente de las
; setas que pueden bloquear la trayectoria del segmento. Cualquier cosa en el
; camino del segmento será borrada.

       inc (ix+1)          ; no ha llegado a la parte inferior, bajar.
       ret

; mover segmento a la parte superior de la pantalla.

segmt  xor a               ; igual que ld a,0 pero ahorra 1 byte.
       ld (ix+1),a         ; nueva coordenada x = inicio de la pantalla.
       ret

; Detección de colisiones segmento.
; Comprueba la existencia de colisiones con el jugador y las balas del jugador.

segcol ld a,(ply)          ; bala posición y.
       cp (ix+2)           ; ¿es idéntico a la coordenada y del segmento?
       jr nz,bulcol        ; coordenada y diferente, mirar la bala en su lugar.
       ld a,(plx)          ; coordenada x del jugador.
       cp (ix+1)           ; ¿igual a la del segmento?
       jr nz,bulcol        ; coordenada x diferente, mirar la bala en su lugar.

; Así que tenemos una colisión con el jugador.

killpl ld (dead),a         ; Establecer indicador de jugador muerto.
       ret

; Vamos a ver de una colisión con el disparo del jugador.

bulcol ld a,(pbx)          ; bala posición x.
       inc a               ; ¿tiene el valor por defecto?
       ret z               ; si, no hay disparo que mirar.
       cp (ix+1)           ; ¿es coordenada x del disparo igual al segmento?
       ret nz              ; no, entonces no hay colisión.
       ld a,(pby)          ; bala posición y.
       cp (ix+2)           ; ¿es igual a la y del segmento?
       ret nz              ; no, esta vez no hay colisión.

; Tenemos una colisión con el disparo del jugador.

       call dbull          ; borrar disparo.
       ld a,22             ; código para AT.
       rst 16
       ld a,(pbx)          ; coordenada vertical de la bala del jugador.
       inc a               ; 1 linea abajo.
       rst 16              ; establecer posición vertical de la seta.
       ld a,(pby)          ; posición horizontal de la bala.
       rst 16              ; establecer la coordenada horizontal.
       ld a,16             ; código ASCII para controlar tinta.
       rst 16
       ld a,4              ; 4 = color verde.
       rst 16              ; ¡tenemos todas las setas de ese color!
       ld a,145            ; UDG 'B' es el gráfico de la seta.
       rst 16              ; poner la seta en la pantalla.
       call kilbul         ; matar el proyectil.
       ld (ix),a           ; matar al segmento.
       ld hl,numseg        ; numero de segmentos.
       dec (hl)            ; decrementarlo.
       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.
pbx    defb 255            ; coordenadas del disparo del jugador.
pby    defb 255
dead   defb 0              ; flag - el jugador muere cuando no sea cero.

; 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.
       defb 24,126,126,255,255,126,126,24  ; segmento.
       defb 0,102,102,102,102,102,102,0    ; proyectil del jugador.

; Tabla de segmentos.
; Formato: 3 bytes por entrada, 10 segmentos.
; byte 1: 255=segmento apagado, 0=izquierda, 1=derecha.
; byte 2: coordenada x (vertical).
; byte 3: coordenada y (horizontal).

segmnt defb 0,0,0          ; segmento 1.
       defb 0,0,0          ; segmento 2.
       defb 0,0,0          ; segmento 3.
       defb 0,0,0          ; segmento 4.
       defb 0,0,0          ; segmento 5.
       defb 0,0,0          ; segmento 6.
       defb 0,0,0          ; segmento 7.
       defb 0,0,0          ; segmento 8.
       defb 0,0,0          ; segmento 9.
       defb 0,0,0          ; segmento 10.
       


NdT: Esta es la última versión del juego, a partir de este punto lo que se habla en el resto del documento ya no se aplica a este juego, que se da por finalizado.

Pero, un momento, ¿por qué hay dos pruebas de detección de colisiones en lugar de una? Bueno, imagina que la base de la pistola del jugador está una celda de carácter a la izquierda de un segmento del ciempiés. El jugador se mueve a la derecha y el segmento se mueve hacia la izquierda. En la siguiente trama el segmento se movería a la celda ocupada por el jugador, y el jugador se movería a la posición ocupada por el segmento en el cuadro anterior, jugador y segmento de ciempiés pasarían directamente uno a través del otro y una comprobación de detección de colisión no puede dejar de tener esto en cuenta. Mediante la comprobación de colisión después de que el jugador se mueve, y luego otra vez después de que los segmentos de ciempiés se han movido, podemos evitar el problema.

Colisiones entre Sprites

Esto es suficiente para nuestro juego, pero la mayoría de juegos de Spectrum utilizan sprites en lugar de UDG, por lo que en el siguiente capítulo veremos cómo se pueden usar los sprites. Para la detección de colisión, el mismo principio de comprobación de coordenadas se puede utilizar para detectar colisiones entre sprites. Restar las coordenadas del primero de los sprites de los del segundo, verificar la diferencia y si está dentro de la gama de tamaños de los dos sprites combinados tenemos una colisión en ese eje. Una prueba de colisión simple para dos sprites de 16x16 pixel podría ser algo como esto:

; Verificar (l,h) para su colisión con (c,b), aplicación estricta.

colx16 ld a,l              ; coordenada x.
       sub c               ; restar x.
       add a,15            ; añadir distancia máxima.
       cp 31               ; ¿está en rango de x?
       ret nc              ; no,  están separados.
       ld a,h              ; coordenada y.
       sub b               ; restar y.
       add a,15            ; añadir distancia máxima.
       cp 31               ; ¿está en rango de y?
       ret                 ; poner el indicador de colisión desde aquí.

Hay una desventaja con este método. Si los sprites no se rellenan por completo a sus límites de 16x16 píxeles la detección de colisiones parece ser demasiado estricta, por lo que las colisiones se producen cuando los sprites están muy cerca, pero no cuando en realidad se están tocando. Una verificación un poco menos sensible implicaría recortar las esquinas de los sprites en una forma más octogonal, sobre todo si tienen tus sprites esquinas redondeadas. El siguiente proceso funciona mediante la adición a las coordenadas x e y de las diferencias y la comprobación de que están por debajo de un cierto límite. Para una colisión entre dos sprites de 16 x 16 la coordenada de máxima distancias son 15 píxeles para cada eje, mediante la comprobación de que las diferencias entre x e y son de 25 o menos estamos efectivamente recortando un triángulo de 5 x 5 x 5 píxeles de cada esquina.

; Verificar (l, h) para colisión con (c, b), recortando las esquinas.

colc16 ld a,l              ; coordenada x.
       sub c               ; restar x.
       jr nc,colc1a        ; ¿resultado positivo?
       neg                 ; pasar negativo a positivo.
colc1a cp 16               ; ¿en el rango de x?
       ret nc              ; no, están separados.
       ld e,a              ; guardar la diferencia.

       ld a,h              ; coordenada y.
       sub b               ; restar y.
       jr nc,colc1b        ; ¿resultado positivo?
       neg                 ; pasar negativo a positivo.
colc1b cp 16               ; ¿en el rango de y?
       ret nc              ; no, están separados.

       add a,e             ; añadir la diferencia en x.
       cp 26               ; ¿solo los 5 pixels de la esquina se tocan?
       ret                 ; actuar cuando hay colisión.

No hay comentarios:

Publicar un comentario