Í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