Í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.
Movimientos de enemigos
Ya tenemos nuestro fondo cargado y permitimos al jugador mover un sprite a su
alrededor, lo que ahora necesitamos son algunos sprites de enemigos que el jugador
deba evitar. Un programador novato podría pelear mucho con esto, pero en realidad
es mucho más simple de lo que parece.
Enemigos que patrullan
El tipo de enemigo más fácil de programar es el que usa un algoritmo fijo a
seguir, o patrulla una ruta predeterminada. Hemos cubierto una de estas técnicas
en el juego del ciempiés anterior. Otro ejemplo muy sencillo es la que se encuentra
en juegos tales como Jet Set Willy, donde un sprite se desplaza en una
sola dirección hasta que llega al final de su zona de patrulla, a continuación
cambia de dirección y se dirige de nuevo a su punto de partida, antes de cambiar
de dirección otra vez e iniciar el ciclo de nuevo. Como se puede imaginar, estas
rutinas son increíblemente fáciles de escribir.
En primer lugar hemos de crear en nuestra tabla con la estructura del alien
una coordenada de posición mínima y máxima, y la dirección actual. Es generalmente
buena idea comentar estas tablas, así que vamos a hacerlo.
; tabla de datos del Alien, 6 bytes por alien. ; ix = tipo de gráfico, como pollo/medusa etc. ; ix + 1 = dirección, 0=arriba, 1=derecha, 2=abajo, 3=izquierda. ; ix + 2 = coordenada x actual. ; ix + 3 = coordenada y actual. ; ix + 4 = mínima coordenada x o y , dependiendo de la dirección. ; ix + 5 = máxima coordenada x o y , dependiendo de la dirección. altab defb 0,0,0,0,0,0 defb 0,0,0,0,0,0 defb 0,0,0,0,0,0
luego para manejar su sprite podríamos escribir algo como esto
ld a,(ix+1) ; dirección del movimiento del alien. rra ; rotar bit bajo con acarreo. jr nc,movav ; si no hay acarreo = 0 o 2, debe ser vertical. ; dirección es 1 o 3 por lo que es horizontal. rra ; rotar siguiente bit con para probar. jr nc,movar ; dirección 1 = mover a la derecha el alien. ; Mover alien a la izquierda. moval ld a,(ix+3) ; obtener la coordenada y. sub 2 ; mover hacia la izquierda. ld (ix+3),a cp (ix+4) ; ¿se ha llegado al mínimo? jr z,movax ; sí, cambio de dirección. jr c,movax ; Hay, nos hemos pasado. ret ; Mover alien a la derecha. movar ld a,(ix+3) ; obtener la coordenada y. add a,2 ; mover hacia la derecha. ld (ix+3),a cp (ix+5) ; ¿se ha llegado al máximo? jr nc,movax ; sí, cambio de dirección. ret ; Mover alien verticalmente. movav rra ; probar la dirección. jr c,movad ; dirección 2 es abajo. ; Mover alien hacia arriba. movau ld a,(ix+2) ; obtener la coordenada x. sub 2 ; mover hacia arriba. ld (ix+2),a cp (ix+4) ; ¿se ha llegado al mínimo? jr z,movax ; sí, cambio de dirección. ret ; Mover alien hacia abajo. movad ld a,(ix+2) ; obtener la coordenada x. add a,2 ; mover hacia abajo. ld (ix+2),a ; nueva coordenada. cp (ix+5) ; ¿se ha llegado al máximo? jr nc,movax ; sí, cambio de dirección. ret ; Cambiar dirección del alien. movax ld a,(ix+1) ; indicador de dirección. xor 2 ; cambiar dirección, ya sea ; horizontal o verticalmente. ld (ix+1),a ; establecer una nueva dirección. ret
Si quisiéramos ir más lejos podríamos introducir una bandera extra en nuestra
tabla, ix + 6, para controlar la velocidad del sprite, y sólo moverlo, por ejemplo,
cada dos cuadros si se establece el indicador. Aunque es simple de escribir
y con bajo uso de memoria, este tipo de movimiento es bastante básico, predecible
y de uso limitado. Para enemigos con patrullaje más complicado, por ejemplo,
las olas de ataque de alienígenas en un shoot-em-up, necesitamos tablas de coordenadas,
y mientras que el código es también fácil de escribir, coordinan las tablas
rápidamente se come la memoria, sobre todo si se almacenan ambas coordenadas
x e y. Para acceder a una tabla de este tipo se necesitan dos
bytes por sprites que actúan como un puntero a la tabla de coordenadas.
Una sección típica de código se vería así:
; NdT: little endian = primero byte bajo
ld l,(ix+2) ; puntero de byte bajo, little endian.
ld h,(ix+3) ; puntero byte alto.
ld c,(hl) ; poner coordenada x en c.
inc hl ; punto a la coordenada y.
ld b,(hl) ; poner la coordenada y en b.
inc hl ; apuntar a la siguiente posición.
ld (ix+2),l ; siguiente puntero al byte bajo.
ld (ix+3),h ; siguiente puntero al byte alto.
A continuación un ejemplo un poco más complicado, muestra una oleada de 8 naves de ataque usando una tabla de coordenadas verticales. La posición horizontal de cada sprite se mueve a la izquierda a una velocidad constante de 2 píxeles por imagen, así que no hay necesidad de preocuparse de guardarla. Se utiliza la rutina de sprites precargados del capítulo 8, de manera que los sprites son un poco "peliculeros", pero eso no es importante ahora.
mloop halt ; esperar al haz de TV. ld ix,entab ; apuntar a las naves impares. call mship ; mover las naves. halt ld ix,entab+4 ; apuntar a las naves pares. call mship ; mover las naves de nuevo. call gwave ; generar ondas frescas. jp mloop ; volver al inicio del bucle. ; Mover las naves enemigas. mship ld b,4 ; Número a procesar. mship0 push bc ; guardar contador. ld a,(ix) ; obtener puntero bajo. ld l,a ; ponerlo en l. ld h,(ix+1) ; obtener byte alto. or h ; comprobar que el puntero está configurado. and a ; ¿lo está? call nz,mship1 ; sí, procesarlo. ld de,8 ; saltar a la siguiente solo-pero-una entrada. add ix,de ; apuntar al siguiente enemigo. pop bc ; restaurar contador. djnz mship0 ; repetir para todos los enemigos. ret mship1 push hl ; guardar puntero a la coordenada. call dship ; Eliminar esta nave. pop hl ; restaurar coordenadas. ld a,(hl) ; recuperar la siguiente coordenada. inc hl ; mover el puntero allí. ld (ix),l ; nuevo puntero al byte bajo. ld (ix+1),h ; puntero al byte alto. ld (ix+2),a ; establecer coordenada x. ld a,(ix+3) ; recuperar la posición horizontal. sub 2 ; mover hacia la izquierda 2 píxeles. ld (ix+3),a ; establecer nueva posición. cp 240 ; ¿alcanzamos el borde de la pantalla? jp c,dship ; no por el momento, mostrar en la nueva posición. xor a ; poner a cero el acumulador. ld (ix),a ; borrar el byte bajo del puntero. ld (ix+1),a ; limpiar el byte alto del puntero. ld hl,numenm ; número de enemigos en pantalla. dec (hl) ; uno menos a los que hacer frente. ret gwave ld hl,shipc ; contador de naves. dec (hl) ; una menos. ld a,(hl) ; comprobar nuevo valor. cp 128 ; ¿esperando el siguiente ataque? jr z,gwave2 ; ataque es inminente por lo que hay que configurarlo. ret nc ; si. and 7 ; ¿es hora de generar una nueva nave? ret nz ; aún no lo es ld ix,entab ; tabla de enemigos. ld de,4 ; tamaño de cada entrada. ld b,8 ; Número a comprobar. gwave0 ld a,(ix) ; byte bajo del puntero. ld h,(ix+1) ; byte alto. or h ; ¿están a cero? jr z,gwave1 ; sí, esta entrada está vacía. add ix,de ; apuntar a la siguiente nave. djnz gwave0 ; repetir hasta que encontremos una. ret gwave2 ld hl,wavnum ; presentar el numero de la oleada. ld a,(hl) ; recuperar la configuración actual. inc a ; siguiente a lo largo. and 3 ; empezar de nuevo después de la 4ª oleada. ld (hl),a ; escribir nuevo ajuste. ret gwave1 ld hl,numenm ; número de enemigos en pantalla. inc (hl) ; uno más con que pelear. ld a,(wavnum) ; numero de oleada. ld hl,wavlst ; punteros de datos de oleadas. rlca ; multiplico por 2. rlca ; multiplico por 4. ld e,a ; desplazamiento en e. ld d,0 ; sin byte alto. add hl,de ; encontrar la dirección de la oleada. ld a,(shipc) ; contador de naves. and 8 ; ¿ataque par o impar? rrca ; hacerlo múltiplo de 2 en consecuencia. rrca ld e,a ; desplazamiento en e. ld d,0 ; sin byte alto. add hl,de ; apunto a la primera o a la segunda mitad del ataque. ld e,(hl) ; byte bajo del puntero del ataque. inc hl ; segundo byte. ld d,(hl) ; byte alto del puntero del ataque. ld (ix),e ; byte bajo del puntero. ld (ix+1),d ; byte alto. ld a,(de) ; Recuperar la primera coordenada. ld (ix+2),a ; establecer x. ld (ix+3),240 ; comenzar en el borde derecho de la pantalla. ; Visualización de las naves enemigas. dship ld hl,shipg ; Dirección de los sprites. ld b,(ix+3) ; coordenada y. ld c,(ix+2) ; coordenada x. ld (xcoord),bc ; establecer coordenadas de la rutina de sprites. jp sprite ; llamar a la rutina de sprites. shipc defb 128 ; contador de naves. numenm defb 0 ; número de enemigos. ; Coordenadas de las oleadas de ataque. ; Sólo la coordenada vertical se almacena ya que todas las naves ; se mueven hacia la izquierda 2 píxeles en cada fotograma. coord0 defb 40,40,40,40,40,40,40,40 defb 40,40,40,40,40,40,40,40 defb 42,44,46,48,50,52,54,56 defb 58,60,62,64,66,68,70,72 defb 72,72,72,72,72,72,72,72 defb 72,72,72,72,72,72,72,72 defb 70,68,66,64,62,60,58,56 defb 54,52,50,48,46,44,42,40 defb 40,40,40,40,40,40,40,40 defb 40,40,40,40,40,40,40,40 defb 38,36,34,32,30,28,26,24 defb 22,20,18,16,14,12,10,8 defb 6,4,2,0,2,4,6,8 defb 10,12,14,16,18,20,22,24 defb 26,28,30,32,34,36,38,40 coord1 defb 136,136,136,136,136,136,136,136 defb 136,136,136,136,136,136,136,136 defb 134,132,130,128,126,124,122,120 defb 118,116,114,112,110,108,106,104 defb 104,104,104,104,104,104,104,104 defb 104,104,104,104,104,104,104,104 defb 106,108,110,112,114,116,118,120 defb 122,124,126,128,130,132,134,136 defb 136,136,136,136,136,136,136,136 defb 136,136,136,136,136,136,136,136 defb 138,140,142,144,146,148,150,152 defb 154,156,158,160,162,164,166,168 defb 170,172,174,176,174,172,170,168 defb 166,164,162,160,158,156,154,152 defb 150,148,146,144,142,140,138,136 ; Lista de oleadas de ataque. wavlst defw coord0,coord0,coord1,coord1 defw coord1,coord0,coord0,coord1 wavnum defb 0 ; puntero actual a la oleada wave pointer. ; Sprite de la nave. shipg defb 248,252,48,24,24,48,12,96,24,48,31,243,127,247,255,247 defb 255,247,127,247,31,243,24,48,12,96,24,48,48,24,248,252 sprit7 xor 7 ; complementa los últimos 3 bits. inc a ; ¡agrega uno por suerte! sprit3 rl d ; rotar izquierda... rl c ; ...en el centro del byte... rl e ; ...y a la izquierda de la celda de carácter. dec a ; contar los cambios que hemos hecho. jr nz,sprit3 ; regresar hasta que los movimientos estén completos. ; Línea de la imagen de sprite ahora en e+c+d, lo necesitamos en forma c+d+e ld a,e ; borde izquierdo de la imagen está en e. ld e,d ; poner borde derecho en su lugar. ld d,c ; bit central va en d ld c,a ; y el borde izquierdo de nuevo en c. jr sprit0 ; hemos hecho el cambio para transferir a la pantalla. sprite ld a,(xcoord) ; dibuja el sprite (hl). ld (tmp1),a ; guardar vertical. call scadd ; calcular dirección de la pantalla. ld a,16 ; altura del sprite en pixeles. sprit1 ex af,af' ; guardar el contador de bucles. push de ; guardar dirección de pantalla. ld c,(hl) ; primer gráfico del sprite. inc hl ; incrementar el puntero de datos del sprite. ld d,(hl) ; siguiente bit de la imagen del sprite. inc hl ; apuntar a la siguiente fila de datos del sprite. ld (tmp0),hl ; guardar en tmp0 para más adelante. ld e,0 ; byte derecho en blanco por ahora. ld a,b ; b guarda la posición y. and 7 ; ¿estamos a caballo entre celdas de caracteres? jr z,sprit0 ; no estamos a caballo, no molesta al desplazamiento. cp 5 ; ¿necesitamos 5 o más desplazamientos a la derecha? jr nc,sprit7 ; sí, desplazar a la izquierda que es más rápido. and a ; hay, la bandera de acarreo se establece. sprit2 rr c ; rotar a la izquierda el byte derecho... rr d ; ...Hasta el byte medio... rr e ; ...la byte derecho. dec a ; un turno menos que hacer. jr nz,sprit2 ; repetir hasta que todos los turnos estén completos. sprit0 pop hl ; sacar la dirección de la pantalla en la pila. ld a,(hl) ; ya está lista. xor c ; fusionar con los datos de la imagen. ld (hl),a ; colocar en la pantalla. inc l ; siguiente celda de carácter por la derecha. ld a,(hl) ; ya estaba antes. xor d ; fusionarse con el centro de la imagen. ld (hl),a ; poner de nuevo en la pantalla. inc hl ; siguiente bit del área de la pantalla. ld a,(hl) ; lo que ya está allí. xor e ; borde derecho de los datos de imagen del sprite. ld (hl),a ; poner en pantalla. ld a,(tmp1) ; coordenada vertical temporal. inc a ; siguiente línea de abajo. ld (tmp1),a ; almacenar nueva posición. and 63 ; ¿nos movemos al siguiente tercio de pantalla? jr z,sprit4 ; sí, encontrar el próximo segmento. and 7 ; ¿entrando en celda de carácter siguiente? jr z,sprit5 ; Sí, encuentre siguiente fila. dec hl ; izquierda 2 bytes. dec l ; es está el límite a horcajadas de 256 bytes aquí. inc h ; siguiente fila de esta celda de carácter. sprit6 ex de,hl ; Dirección de pantalla en de. ld hl,(tmp0) ; restaurar la dirección del gráfico. ex af,af' ; restaurar el contador del bucle. dec a ; decrementarlo. jp nz,sprit1 ; no alcanzado el borde inferior del sprite, repetir. ret ; trabajo hecho. sprit4 ld de,30 ; el siguiente segmento es de 30 bytes. add hl,de ; añadir a la dirección de la pantalla. jp sprit6 ; repetir. sprit5 ld de,63774 ; menos 1762. add hl,de ; restar 1762 de la dirección física de la pantalla. jp sprit6 ; volver al bucle. scadd ld a,(xcoord) ; recuperar la coordenada vertical. ld e,a ; guardarla en e. ; Encuentra línea dentro de la celda. and 7 ; línea 0-7 en el cuadrado de caracteres. add a,64 ; 64 * 256 = 16384 = inicio de la pantalla. ld d,a ; d = línea * 256. ; Encuentra en que tercio de la pantalla estamos. ld a,e ; restaurar la vertical. and 192 ; segmento 0, 1 o 2 multiplicamos por 64. rrca ; dividirlo por 8. rrca rrca ; segmento 0-2 multiplicado por 8. add a,d ; sumar a+d obtiene dirección de inicio del segmento. ld d,a ; Encuentra la celda de carácter dentro del segmento. ld a,e ; 8 casillas de caracteres por segmento. rlca ; dividir x por 8 y multiplicarlo por 32, rlca ; siguiente cálculo: multiplicar por 4. and 224 ; enmascarar los bits que no queremos. ld e,a ; cálculo de la coordenada vertical completo. ; Agregar el elemento horizontal. ld a,(ycoord) ; coordenada y. rrca ; sólo es necesario dividir por 8. rrca rrca and 31 ; cuadrados 0 a 31 a través de la pantalla. add a,e ; añadir al total de la medida. ld e,a ; de = dirección de la pantalla. ret xcoord defb 0 ; coordenada de pantalla. ycoord defb 0 ; coordenada de pantalla. tmp0 defw 0 ; espacio de trabajo. tmp1 defb 0 ; posición vertical temporal. ; tabla de naves enemigas, 8 entradas x 4 bytes cada una. entab defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0 defb 0,0,0,0
Enemigos inteligentes
Hasta ahora nos hemos ocupado de drones predecibles, pero ¿queremos dar al jugador
la ilusión de que los sprites enemigos piensan por sí mismos? Una vía que podemos
utilizar para esto sería para darles una decisión totalmente al azar en el momento
de su creación.
Aquí está el código fuente de Turbomania, un juego escrito originalmente
para la 1K coding competition (concursos de código en 1K) de 2005. Es
muy simple, pero incorpora movimiento puramente aleatorio. Los coches enemigos
viajan en una dirección hasta que ya no se pueden mover, a continuación seleccionan
otra dirección al azar. Además, un coche puede cambiar de dirección al azar
incluso si puede continuar en su dirección actual. Es muy primitivo por supuesto,
solo echa un vistazo a la rutina MCAR y verá exactamente lo quiero decir.
org 24576 ; Constantes. YELLOW equ 49 ; atributo de color amarillo normal. YELLOB equ YELLOW + 64 ; atributo de color amarillo brillante. ; Código principal del juego. ; Borrar la pantalla para dar color verde alrededor de los bordes. ld hl,23693 ; variable de sistema para atributos. ld (hl),36 ; quiero fondo verde. waitk ld a,(23560) ; leer teclado. cp 32 ; ¿pulsado ESPACIO? jr nz,waitk ; no, espera. call nexlev ; jugar, jr waitk ; ESPACIO para reiniciar el juego. ; Borrar los datos por nivel. nexlev call 3503 ; borrar la pantalla. ld hl,rmdat ; Datos de las salas. ld de,rmdat+1 ld (hl),1 ; configurar un bloque en la sombra. ld bc,16 ; longitud de sala menos el primer byte. ldir ; copiar al resto de la primera fila. ld bc,160 ; longitud de la sala menos la primera fila. ld (hl),b ; borrar primer byte. ldir ; borrar datos de la sala. ; Configurar los bloques predeterminados. ld c,15 ; última posición del bloque. popbl0 ld b,9 ; última fila. popbl1 call filblk ; llenar el bloque. dec b ; una columna hacia arriba. jr z,popbl2 ; columna hecha, seguir adelante. dec b ; y de nuevo. jr popbl1 popbl2 dec c ; moverse en la fila. jr z,popbl3 ; columna completa, seguir adelante. dec c ; siguiente fila. jr popbl0 ; Ahora dibujar los bits únicos para este nivel. popbl3 ld b,7 ; número de bloques para insertar. popbl5 push bc ; guardar contador. call random ; obtener un número aleatorio. and 6 ; números en el rango 0-6 por favor. add a,2 ; cambiar a 2-8. ld b,a ; esa es la columna. popbl4 call random ; otro número. and 14 ; buscamos números pares 0-12. cp 14 ; ¿más alto de lo que queremos? jr nc,popbl4 ; si, inténtelo de nuevo. inc a ; colocarlo en el rango 1-13. ld c,a ; esa es la fila. call filblk ; llenar bloque. popbl6 call random ; otro número aleatorio. and 14 ; Sólo queremos 0-8. cp 9 ; ¿por encima de número que queremos? jr nc,popbl6 ; inténtelo de nuevo. inc a ; convertirlo a 1-9. ld b,a ; coordenada vertical. call random ; obtener bloque horizontal. and 14 ; par, 0-14. ld c,a ; posición y. call filblk ; llenar en ese cuadrado. pop bc ; restaurar el contador. djnz popbl5 ; uno menos que hacer. xor a ; cero. ld hl,playi ; dirección deseada del jugador. ld (hl),a ; dirección por defecto. inc hl ; apuntar a la dirección a presentar. ld (hl),a ; dirección por defecto. inc hl ; siguiente dirección del jugador. ld (hl),a ; dirección por defecto. out (254),a ; establecer el color del borde mientras tanto. call atroom ; mostrar disposición del nivel actual. ld hl,168+8*256 ; coordenadas. ld (encar2+1),hl ; establecer la posición del segundo coche. ld h,l ; coordenada y a la derecha. ld (encar1+1),hl ; establecer la posición del primer coche. ld l,40 ; x en la parte superior de la pantalla. ld (playx),hl ; iniciar al jugador aquí. ld hl,encar1 ; primer coche. call scar ; presentarlo. ld hl,encar2 ; segundo coche. call scar ; presentarlo. call dplayr ; mostrar el sprite del jugador. call blkcar ; hacer el coche del jugador de color negro. ; Retardo de dos segundos antes de empezar. ld b,100 ; longitud del retardo. waitt halt ; esperar una interrupción. djnz waitt ; repetir. mloop halt ; haz de electrones en la parte superior izquierda. call dplayr ; Eliminar jugador. ; Poner como atributo tinta azul de nuevo. call gpatts ; obtener atributo del jugador. defb 17,239,41 ; retirar el fondo verde, añadir fondo y tinta azul. call attblk ; establecer el color de la carretera. ; Mover el coche del jugador. ld a,(playd) ; dirección . ld bc,(playx) ; coordenadas del jugador. call movc ; mover coordenadas. ld hl,(dispx) ; nuevas coordenadas. ld (playx),hl ; establecer una nueva posición del jugador. ; ¿Podemos cambiar de dirección? ld a,(playi) ; dirección deseada del jugador. ld bc,(playx) ; coordenadas del jugador. call movc ; mover coordenadas. call z,setpn ; establecer la nueva dirección del jugador. ; Cambia la dirección. ld a,(nplayd) ; nueva dirección del jugador. ld (playd),a ; fijar la dirección actual. call dplayr ; volver a mostrar en la nueva posición. ; Establecer los atributos de los coches. call blkcar ; hacer el coche del jugador negro. ; Controles. ld a,239 ; fila del teclado 6-0 = 61438. ld e,1 ; dirección derecha. in a,(254) ; leer teclas. rra ; ¿jugador mueve a la derecha? call nc,setpd ; sí, establecer la dirección del jugador. ld e,3 ; dirección izquierda. rra ; ¿jugador movió a la izquierda call nc,setpd ; sí, establecer la dirección del jugador. ld a,247 ; 63486 es el puerto para la fila 1-5. ld e,0 ; dirección hacia arriba. in a,(254) ; leer teclas. and 2 ; comprobar segunda tecla en (2). call z,setpd ; establecer la dirección. ld a,251 ; 64510 es el puerto para la fila Q-T. ld e,2 ; dirección hacia abajo. in a,(254) ; leer teclas. and e ; comprobar segunda tecla del borde (W).. call z,setpd ; establecer la dirección. ; Coches enemigos. ld hl,encar1 ; coche del enemigo 1. push hl ; guardar puntero. call procar ; procesar el coche. pop hl ; recuperar el puntero del coche. call coldet ; comprobar colisiones. ld hl,encar2 ; coche del enemigo 2. push hl ; guardar puntero. halt ; sincronizar con pantalla. call procar ; procesar el coche. pop hl ; recuperar el puntero del coche. call coldet ; comprobar colisiones. ; Contar espacio amarillo restante. ld hl,22560 ; dirección. ld bc,704 ; atributos que contar. ld a,YELLOB ; atributos que estamos buscando. cpir ; contar caracteres. ld a,b ; byte alto del resultado. or c ; combinan con el byte bajo. jp z,nexlev ; nada a la izquierda, ir al siguiente nivel. ; Fin del bucle principal. jp mloop ; Coche negro sobre fondo cian. blkcar call gpatts ; obtener atributos de los jugadores. defb 17,232,40 ; quitar el fondo rojo/tinta azul, añadir fondo azul. ; Establecer los atributos de bloque de 16x16 píxeles. attblk call attlin ; pintar línea horizontal. call attlin ; pintar otra línea. ld a,c ; posición vertical. and 7 ; ¿está a caballo entre celdas? ret z ; no, entonces no hay tercera línea. attlin call setatt ; pintar la carretera. call setatt ; y otra vez. ld a,b ; posición horizontal. and 7 ; ¿a caballo entre los bloques? jr z,attln0 ; no, dejar tercera celda como está. call setatt ; establecer atributo. dec l ; atrás de nuevo una celda. attln0 push de ; conservar los colores. ld de,30 ; distancia al siguiente. add hl,de ; puntero a la siguiente fila hacia abajo. pop de ; restaurar máscaras de color. ret ; Establecer el atributo de una sola celda. setatt ld a,(hl) ; recuperar el atributo de la celda que lo contiene. and e ; eliminar los elementos de color en el registro c. or d ; añadir los de b para formar nuevo color. ld (hl),a ; establecer color. inc l ; siguiente celda. ret ; Detección de colisiones, basada en coordenadas. coldet call getabc ; obtener coordenadas. ld a,(playx) ; posición horizontal. sub c ; comparar con el coche x. jr nc,coldt0 ; resultado fue positivo. neg ; era negativo, revertir el signo. coldt0 cp 16 ; ¿dentro de los 15 píxeles? ret nc ; no hubo colisión. ld a,(playy) ; jugador y. sub b ; comparar con el coche y. jr nc,coldt1 ; resultado fue positivo. neg ; era negativo, revertir el signo. coldt1 cp 16 ; ¿dentro de los 15 píxeles? ret nc ; no hubo colisión. pop de ; eliminar la dirección de retorno de la pila. ret setpd ex af,af' ld a,e ; dirección. ld (playi),a ; fijar la dirección prevista. ex af,af' ret setpn ld a,(playi) ; nueva dirección deseada. ld (nplayd),a ; establecer siguiente dirección. ret ; Mover las coordenadas del sprites en la dirección correspondiente. movc ld (dispx),bc ; posición por defecto. and a ; dirección 0. jr z,movcu ; mover hacia arriba. dec a ; dirección 1. jr z,movcr ; mover hacia arriba. dec a ; dirección 2. jr z,movcd ; mover hacia arriba. movcl dec b ; izquierda un píxel. dec b ; izquierda de nuevo. movc0 call chkpix ; comprobar los atributos de píxeles. ld (dispx),bc ; nuevas coordenadas. ret movcu dec c ; arriba un pixel. dec c ; y de nuevo. jr movc0 movcr inc b ; derecha un píxel. inc b ; derecha de nuevo. jr movc0 movcd inc c ; abajo un píxel. inc c ; una vez más. jr movc0 ; Comprobar los atributos del pixel por colisión. ; Cualquier celda con tinta verde es sólidas. chkpix call ataddp ; obtener la dirección del atributo de píxeles. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,(hl) ; obtener atributos. and 4 ; comprobar colores de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,b ; posición horizontal. and 7 ; ¿a caballo entre las celdas? jr z,chkpx1 ; no, mirar abajo entonces. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. chkpx1 ld de,30 ; distancia hasta la siguiente celda hacia abajo. add hl,de ; apuntar aquí. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,b ; posición horizontal. and 7 ; ¿a caballo entre las celdas? jr z,chkpx2 ; no, mirar abajo entonces. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. chkpx2 ld a,c ; distancia desde la parte superior de la pantalla. and 7 ; ¿estamos a caballo entre celdas verticales? ret z ; no, el movimiento es bueno. add hl,de ; apuntar allí. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. inc hl ; siguiente cuadrado a la derecha. ld a,b ; posición horizontal. and 7 ; ¿a caballo entre las celdas? ret z ; no, el movimiento es correcto. ld a,(hl) ; obtener atributos. and 4 ; comprobar color de la tinta. jr nz,chkpx0 ; inválida, bloquear el movimiento. ret ; adelante. chkpx0 pop de ; eliminar la dirección de retorno de la pila. ret ; Llenar un bloque en el mapa. ; Obtener dirección del bloque. filblk ld a,b ; número de fila. rlca ; multiplicar por 16. rlca rlca rlca add a,c ; añadir al desplazamiento total. ld e,a ; este desplazamiento es pequeño. ld d,0 ; no necesita byte alto. ld hl,rmdat ; dirección de datos de la sala. add hl,de ; añadir al bloque de dirección. ; dirección de bloque está en hl, vamos a llenarlo. ld (hl),2 ; poner el bloque en on. ld de,16 ; distancia al siguiente bloque de abajo. add hl,de ; apuntar allí. ld a,(hl) ; chequearlo. and a ; ¿está establecido? ret nz ; sí, no sobrescribir. ld (hl),1 ; ajustar la sombra. ret ; Dibujar una pantalla compuesta enteramente de bloques de atributos. atroom ld hl,rmdat ; Datos de la sala. ld a,1 ; comenzar en la fila 1. ld (dispx),a ; establecer coordenadas. ld b,11 ; contar filas. atrm0 push bc ; guardar el contador. ld b,15 ; contador de columnas. ld a,1 ; número de columna. ld (dispy),a ; ajustado a la izquierda de la pantalla. atrm1 push bc ; guardar el contador. ld a,(hl) ; obtener siguiente tipo de bloque. push hl ; guardar dirección de datos. rlca ; numero de bloque doblado. rlca ; y de nuevo para múltiplo de 4. ld e,a ; desplazamiento para dirección del bloque. ld d,0 ; no necesita byte alto. ld hl,blkatt ; atributos del bloque. add hl,de ; apuntar al bloque que queremos. call atadd ; obtener dirección de la posición de la pantalla. ldi ; transferir primer bloque. ldi ; y el segundo. ld bc,30 ; distancia hasta la siguiente fila. ex de,hl ; conmutar celda y dirección de pantalla. add hl,bc ; apuntar a la siguiente fila por abajo. ex de,hl ; intercambiarlas de nuevo. ldi ; hacer la tercera celda. ldi ; atributo de la cuarta celda. ld hl,dispy ; número de columna. inc (hl) ; moverse a través de una celda. inc (hl) ; y otra. pop hl ; restaurar la dirección de la sala. pop bc ; restablecer contador de columna. inc hl ; apuntar al siguiente bloque. djnz atrm1 ; hacer resto de la hilera. inc hl ; saltar un carácter, las líneas son múltiplos 16. ld a,(dispx) ; posición vertical. add a,2 ; mirar 2 celdas hacia abajo. ld (dispx),a ; nueva fila. pop bc ; restablecer contador de columna. djnz atrm0 ; hacer las filas restantes. ret ; Atributos del bloque de fondo. blkatt defb YELLOB,YELLOB ; espacio. defb YELLOB,YELLOB defb YELLOW,YELLOW ; espacio sombreado. defb YELLOB,YELLOB defb 124,68 ; Modelo blanco/negro bandera de cuadros. defb 68,124 ; Calcular la dirección del atributo de carácter en (dispx, dispy). atadd push hl ; necesitamos preservar el par hl. ld hl,(dispx) ; coordenadas a comprobar, en coordenadas de carácter. add hl,hl ; multiplicar x e y por 8. add hl,hl add hl,hl ld b,h ; copiar coordenada y a b. ld c,l ; poner coordenada x en c. call ataddp ; obtener la dirección del pixel. ex de,hl ; poner la dirección en de. pop hl ; restaurar hl. ret ; Obtener atributos del jugador. gpatts ld bc,(playx) ; coordenadas del jugador. ; Calcular la dirección del atributo para el píxel en (c, b). ataddp ld a,c ; mira primero la vertical. rlca ; divida por 64. rlca ; mas rápido que 6 operaciones rrca. ld l,a ; almacenar en el registro l por ahora. and 3 ; enmascarar para encontrar el segmento. add a,88 ; los atributos comienzan a partir de 88*256=22528. ld h,a ; así está ordenado el byte alto. ld a,l ; vertical/64 es igual que vertical*4. and 224 ; buscamos un múltiplo de 32. ld l,a ; elemento vertical calculado. ld a,b ; obtener la posición horizontal. rra ; dividir por 8. rra rra and 31 ; queremos el resultado en el rango 0-31. add a,l ; añadir a byte bajo existente. ld l,a ; completado el byte bajo. ld a,(hl) ; obtener contenido de la celda. ret ; dirección del atributo ahora en hl. ; Mover el coche - cambio de dirección requerida. mcarcd ld a,(hl) ; dirección actual. inc a ; gira en el sentido de las manecillas del reloj. and 3 ; sólo 4 direcciones. ld (hl),a ; nueva dirección. ; Mover un coche enemigo. mcar push hl ; preservar puntero al coche. call getabc ; recuperar coordenadas y dirección. call movc ; mover el coche. pop hl ; refrescar puntero del coche jr nz,mcarcd ; no se puede moverse mas, dar la vuelta. inc hl ; apuntar a x. ld a,c ; guardar posición x en c. ld (hl),a ; posición x. inc hl ; apuntar a y. ld (hl),b ; nueva colocación. or b ; combinar ambos. and 31 ; encontrar celdas de posición a caballo. cp 8 ; ¿estamos en un punto de giro válido? ret nz ; no, no se puede cambiar de dirección. ld a,r ; falso número aleatorio. cp 23 ; compruebe que está por debajo de este valor. ret nc ; no lo está, no cambia. push hl ; guardar puntero del coche. call random ; obtener número aleatorio. pop hl ; restaurar coche. dec hl ; volver a la coordenada x. dec hl ; volver de nuevo a la dirección. and 3 ; dirección en el rango de 0-3. ld (hl),a ; nueva dirección. ret ; Obtener coordenadas de automóviles y su dirección. getabc ld a,(hl) ; obtener dirección. inc hl ; apuntar a la posición x. ld c,(hl) ; coordenada x. inc hl ; apuntar a y. ld b,(hl) ; posición y. ret ; Procesar coche en el punto hl. procar push hl ; guardar puntero. push hl ; guardar puntero. call scar ; Eliminar coche. pop hl ; restablecer puntero a coche. call mcar ; mover coche. pop hl ; restablecer puntero a coche. ; Mostrar coche enemigo. scar call getabc ; obtener coordenadas y dirección. jr dplay0 ; Presentar sprite del jugador. dplayr ld bc,(playx) ; coordenadas del jugador. ld a,(playd) ; dirección del jugador. dplay0 rrca ; multiplicar por 32. rrca rrca ld e,a ; sprite * 32 en el byte bajo. ld d,0 ; sin byte alto. ld hl,cargfx ; gráfico del coche. add hl,de ; añadir desplazamiento al sprite. jr sprite ; mostrar el sprite. ; Esta es la rutina de sprites y espera las coordenadas en la forma (c, b), ; donde c es la coordenada vertical desde la parte superior de la pantalla ; (0-176), y b es la coordenada horizontal desde la izquierda de la pantalla ; (0 a 240). Los datos del sprite se almacenan como se espera en su forma no ; desplazada ya que esta rutina se encarga de todo el desplazamiento por sí ; misma. Esto significa que direccionar el sprite no es especialmente rápido, ; pero los gráficos sólo ocupan 1/8 del espacio que requerirían en forma ; pre-desplazada. ; La entrada HL debe apuntar a los datos del sprite sin desplazamiento. sprit7 xor 7 ; complementa los últimos 3 bits. inc a ; ¡agrega uno por suerte! sprit3 rl d ; rotar izquierda... rl c ; ...en el centro del byte... rl e ; ...y a la izquierda de la celda de carácter. dec a ; contar los cambios que hemos hecho. jr nz,sprit3 ; regresar hasta que los movimientos estén completos. ; Línea de la imagen de sprite ahora en e+c+d, lo necesitamos en forma c+d+e ld a,e ; borde izquierdo de la imagen está en e. ld e,d ; poner borde derecho en su lugar. ld d,c ; bit central va en d ld c,a ; y el borde izquierdo de nuevo en c. jr sprit0 ; hemos hecho el cambio para transferir a la pantalla. sprite ld (dispx),bc ; guardar ahora coordenadas en dispx. call scadd ; calcular dirección de la pantalla. ld a,16 ; altura del sprite en pixeles. sprit1 ex af,af' ; guardar el contador de bucles. push de ; guardar dirección de pantalla. ld c,(hl) ; primer gráfico del sprite. inc hl ; incrementar el puntero de datos del sprite. ld d,(hl) ; siguiente bit de la imagen del sprite. inc hl ; apuntar a la siguiente fila de datos del sprite. ld (sprtmp),hl ; guardar para más adelante. ld e,0 ; byte derecho en blanco por ahora. ld a,b ; b guarda la posición y. and 7 ; ¿estamos a caballo entre celdas de caracteres? jr z,sprit0 ; no estamos a caballo, no molesta al desplazamiento. cp 5 ; ¿necesitamos 5 o más desplazamientos a la derecha? jr nc,sprit7 ; sí, desplazar a la izquierda que es más rápido. and a ; hay, la bandera de acarreo se establece. sprit2 rr c ; rotar a la izquierda el byte derecho... rr d ; ...Hasta el byte medio... rr e ; ...la byte derecho. dec a ; un turno menos que hacer. jr nz,sprit2 ; repetir hasta que todos los turnos estén completos. sprit0 pop hl ; sacar la dirección de la pantalla en la pila. ld a,(hl) ; ya está lista. xor c ; fusionar con los datos de la imagen. ld (hl),a ; colocar en la pantalla. inc l ; siguiente celda de carácter por la derecha. ld a,(hl) ; ya estaba antes. xor d ; fusionarse con el centro de la imagen. ld (hl),a ; poner de nuevo en la pantalla. inc l ; siguiente bit del área de la pantalla. ld a,(hl) ; lo que ya está allí. xor e ; borde derecho de los datos de imagen del sprite. ld (hl),a ; poner en pantalla. ld a,(dispx) ; coordenada vertical temporal. inc a ; siguiente línea de abajo. ld (dispx),a ; almacenar nueva posición. and 63 ; ¿nos movemos al siguiente tercio de pantalla? jr z,sprit4 ; sí, encontrar el próximo segmento. and 7 ; ¿entrando en celda de carácter siguiente? jr z,sprit5 ; Sí, encuentre siguiente fila. dec l ; izquierda 2 bytes. dec l ; es está el límite a horcajadas de 256 bytes aquí. inc h ; siguiente fila de esta celda de carácter. sprit6 ex de,hl ; Dirección de pantalla en de. ld hl,(sprtmp) ; restaurar la dirección del gráfico. ex af,af' ; restaurar el contador del bucle. dec a ; decrementarlo. jp nz,sprit1 ; no alcanzado el borde inferior del sprite, repetir. ret ; trabajo hecho. sprit4 ld de,30 ; el siguiente segmento es de 30 bytes. add hl,de ; añadir a la dirección de la pantalla. jp sprit6 ; repetir. sprit5 ld de,63774 ; menos 1762. add hl,de ; restar 1762 de la dirección física de la pantalla. jp sprit6 ; volver al bucle. ; Esta rutina devuelve la dirección de pantalla de (c, b) en de. scadd ld a,c ; obtener la posición vertical. and 7 ; línea 0-7 en el cuadro del carácter. add a,64 ; 64 * 256 = 16384 (inicio de la pantalla) ld d,a ; línea * 256. ld a,c ; obtener verticales de nuevo. rrca ; multiplicar por 32. rrca rrca and 24 ; byte alto del desplazamiento de segmento. add a,d ; añadir a byte alto de pantalla existente. ld d,a ; ese es el byte alto ordenado. ld a,c ; 8 casillas de carácter por segmento rlca ; 8 píxeles por celda, multiplicado por 4 = 32. rlca ; celda x 32 da la posición dentro del segmento. and 224 ; asegurarse de que es un múltiplo de 32. ld e,a ; cálculo de coordenada vertical hecho. ld a,b ; coordenada y. rrca ; Sólo es necesario dividir por 8. rrca rrca and 31 ; cuadrados 0 - 31 a través de la pantalla. add a,e ; añadirse al total de la medida. ld e,a ; hl = dirección de la pantalla. ret ; Generador de números seudo-aleatorio. ; Llevando un puntero a través de la ROM (a partir de una semillas), ; devuelve el contenido del byte en esa ubicación. random ld hl,(seed) ; puntero a la ROM. res 5,h ; mantenerse dentro de los primeros 8K de ROM. ld a,(hl) ; obtener el número "aleatorio" de esa ubicación. xor l ; más aleatoriedad. inc hl ; incrementar el puntero. ld (seed),hl ; nueva posición. ret ; Datos gráficos del Sprite. ; Primero apunta subiendo. cargfx defb 49,140,123,222,123,222,127,254,55,236,15,240,31,248,30,120 defb 29,184,108,54,246,111,255,255,247,239,246,111,103,230,3,192 ; Segunda imagen apunta a la derecha. defb 60,0,126,14,126,31,61,223,11,238,127,248,252,254,217,127 defb 217,127,252,254,127,248,11,238,61,223,126,31,126,14,60,0 ; La tercera apunta hacia abajo. defb 3,192,103,230,246,111,247,239,255,255,246,111,108,54,29,184 defb 30,120,31,248,15,240,55,236,127,254,123,222,123,222,49,140 ; La última el coche apunta a la izquierda. defb 0,60,112,126,248,126,251,188,119,208,31,254,127,63,254,155 defb 254,155,127,63,31,254,119,208,251,188,248,126,112,126,0,60 ; Variables usadas por el juego. org 32768 playi equ $ ; dirección deseada cuando el giro es posible. playd equ playi+1 ; dirección actual del jugador. nplayd equ playd+1 ; siguiente dirección jugador. playx equ nplayd+1 ; coordenada x del jugador playy equ playx+1 ; coordenada y del jugador. encar1 equ playy+1 ; coche enemigo 1. encar2 equ encar1+3 ; coche enemigo 2. dispx equ encar2+3 ; coordenadas de uso general. dispy equ dispx+1 seed equ dispy+1 ; semilla de números aleatorios. sprtmp equ seed+2 ; dirección temporal del sprite. termin equ sprtmp+2 ; fin de variables. rmdat equ 49152
Si has ensamblado este juego y lo has probado, te darás cuenta de que rápidamente
se vuelve aburrido. Es muy fácil mantenerse fuera del alcance de los enemigos
cubriendo un lado de la pista, y luego esperar hasta que se muevan y cubrir
el otro lado. Este algoritmo no tiene aspecto de cazador-asesino por lo que
el jugador nunca es perseguido. Es más, esta rutina es de simples vehículos
que no cambian de dirección sin advertencia. En la mayoría de juegos esto sólo
es aceptable si un sprite llega a un callejón sin salida y no se puede mover
en cualquier otra dirección.
Tal vez debemos en su lugar escribir rutinas en que los aliens interactúan con
el jugador, y lo persiguen. Así, el algoritmo más básico sería seguir una línea
comprobando las coordenadas base x/y, y mover el sprite del alien hacia el jugador.
La rutina siguiente muestra cómo podría lograrse este objetivo, la rutina buscadora
de blancos almov es la que mueve el sprite alrededor del jugador.
Intente guiar al bloque número 1 por la pantalla con las teclas A, S, D y F,
y el bloque número 2 te seguirá alrededor de la pantalla. Sin embargo, al hacer
esto pronto descubrimos el defecto básico con este tipo de persecución, es muy
fácil atrapar el sprite enemigo en una esquina porque la rutina no es lo suficientemente
inteligente para moverse hacia atrás con el fin de conseguir rodear los obstáculos.
; Aleatoriamente cubrir la pantalla con bloques amarillos. ld de,1000 ; dirección en la ROM. ld b,64 ; número de celdas de color. yello0 push bc ; almacenas el registro. ld a,(de) ; obtener la primera coordenada al azar. and 127 ; la mitad de la altura de la pantalla. add a,32 ; al menos 32 píxeles hacia abajo. ld c,a ; coordenada x. inc de ; siguiente byte de ROM. ld a,(de) ; recuperar valor. inc de ; siguiente byte de ROM. ld b,a ; coordenada y. call ataddp ; encuentra la dirección del atributo. ld (hl),48 ; establecer atributos. pop bc ; restaurar el contador del bucle. djnz yello0 ; repetir varias veces. ld ix,aldat ; datos del alien. call dal ; mostrar alien. call dpl ; presentar jugador. mloop halt ; esperar al haz de electrones. call dal ; eliminar alien. call almov ; movimiento del alien. call dal ; mostrar alien. halt ; esperar al haz de electrones. call dpl ; eliminar jugador. call plcon ; controles del jugador. call dpl ; mostrar al jugador. jp mloop ; volver al inicio del bucle principal. aldat defb 0,0,0 ; datos del alien. ; Visualizar/eliminar alien. dal ld c,(ix) ; posición vertical. ld b,(ix+1) ; posición horizontal. ld (xcoord),bc ; establecer las coordenadas del sprite. ld hl,algfx ; gráfico del alien. jp sprite ; XOR del sprite con la pantalla. ; Visualizar/eliminar sprite del jugador. dpl ld bc,(playx) ; coordenadas. ld (xcoord),bc ; establecer las coordenadas de pantalla. ld hl,plgfx ; gráfico del jugador. jp sprite ; xor del sprite dentro o fuera de la pantalla. ; control del jugador. plcon ld bc,65022 ; puerto para la fila del teclado. in a,(c) ; leer teclado. ld b,a ; almacenar el resultado en el registro b. rr b ; verificación la tecla más externa. call nc,mpl ; jugador a la izquierda. rr b ; comprobar siguiente tecla. call nc,mpr ; jugador de la derecha. rr b ; comprobar siguiente tecla. call nc,mpd ; jugador abajo. rr b ; comprobar siguiente tecla. call nc,mpu ; jugador arriba. ret mpl ld hl,playy ; coordenada. ld a,(hl) ; comprobar el valor. and a ; ¿en el borde de la pantalla? ret z ; sí, no se puede mover en esa dirección. sub 2 ; moverse 2 pixeles. ld (hl),a ; nuevo ajuste. ret mpr ld hl,playy ; coordenada. ld a,(hl) ; comprobar el valor. cp 240 ; ¿en el borde de la pantalla? ret z ; sí, no se puede mover en esa dirección. add a,2 ; moverse 2 pixeles. ld (hl),a ; nuevo ajuste. ret mpu ld hl,playx ; coordenada. ld a,(hl) ; comprobar el valor. and a ; ¿en el borde de la pantalla? ret z ; sí, no se puede mover en esa dirección. sub 2 ; moverse 2 pixeles. ld (hl),a ; nuevo ajuste. ret mpd ld hl,playx ; coordenada. ld a,(hl) ; comprobar el valor. cp 176 ; ¿en el borde de la pantalla? ret z ; sí, no se puede mover en esa dirección. add a,2 ; moverse 2 pixeles. ld (hl),a ; nuevo ajuste. ret ; Rutina de movimiento del alien. almov ld a,(playx) ; coordenada x del jugador. ld c,(ix) ; alien x. ld b,(ix+1) ; alien y. cp c ; revisar x del alien jr z,alv0 ; son iguales, seguir en horizontal. jr c,alu ; el alien está más abajo, moverlo hacia arriba. ald inc c ; el alien está más arriba, moverlo hacia abajo. jr alv0 ; Ahora comprobar la posición de las paredes. alu dec c ; mover hacia abajo. alv0 call alchk ; comprobar atributos. cp 56 ; ¿están bien? jr z,alv1 ; Sí, establecer la coordenada x. ld c,(ix) ; restaurar la anterior coordenada x. jr alh ; ahora ir en horizontal. alv1 ld (ix),c ; nueva coordenada x. alh ld a,(playy) ; horizontal del jugador. cp b ; comprobar horizontal del alien. jr z,alok ; son iguales, verificar la colisión. jr c,all ; alien a la derecha, mover a la izquierda. alr inc b ; alien a la izquierda, mover a la derecha. jr alok ; comprobar las paredes. all dec b ; mover a la derecha. alok call alchk ; comprobar atributos. cp 56 ; ¿están bien? ret nz ; no, establecer la nueva coordenada y. ld (ix+1),b ; establecer nueva y. ret ; Comprueba atributos en la posición de alien (c,b). alchk call ataddp ; conseguir dirección del atributo. ld a,3 ; celda de arriba. alchk0 ex af,af' ; guardar el contador de bucles. ld a,(hl) ; verificar color de la celda. cp 56 ; ¿es negro sobre blanco? ret nz ; no, no se puede mover aquí. inc hl ; celda de la derecha. ld a,(hl) ; verificar color de la celda. cp 56 ; ¿es negro sobre blanco? ret nz ; no, no se puede mover aquí. inc hl ; celda de la izquierda. ld a,(hl) ; verificar color de la celda. cp 56 ; ¿es negro sobre blanco? ret nz ; no, no se puede mover aquí. ld de,30 ; distancia hasta la siguiente celda hacia abajo. add hl,de ; mira aquí. ex af,af' ; contador de alturas. dec a ; una menos a la que ir. jr nz,alchk0 ; repetir para todas las filas. ld (ix),c ; establecer nueva x. ld (ix+1),b ; establecer nueva y. ret ; Calcular la dirección del atributo para el píxel en (c,b). ataddp ld a,c ; Mira primero en vertical. rlca ; dividir por 64. rlca ; más rápido que 6 instrucciones rrca. ld l,a ; almacenar en el registro l por ahora. and 3 ; enmascarar para encontrar segmento. add a,88 ; atributos comienzan a partir de 88*256=22528. ld h,a ; ese es nuestro byte alto ordenado. ld a,l ; vertical/64, es lo mismo que vertical*4. and 224 ; deseamos un múltiplo de 32. ld l,a ; elemento vertical calculado. ld a,b ; obtener la posición horizontal. rra ; dividir por 8. rra rra and 31 ; queremos dar lugar a rango 0-31. add a,l ; añadir al byte bajo existente. ld l,a ; tenemos el byte bajo hecho. ret ; dirección de atributo ahora en hl. playx defb 80 ; coordenadas del jugador. playy defb 120 xcoord defb 0 ; coordenadas generales multi propósito. ycoord defb 0 ; Rutina de sprites desplazados. sprit7 xor 7 ; complementa los últimos 3 bits. inc a ; ¡agrega uno por suerte! sprit3 rl d ; rotar izquierda... rl c ; ...en el centro del byte... rl e ; ...y a la izquierda de la celda de carácter. dec a ; contar los cambios que hemos hecho. jr nz,sprit3 ; regresar hasta que los movimientos estén completos. ; Línea de la imagen de sprite ahora en e+c+d, lo necesitamos en forma c+d+e ld a,e ; borde izquierdo de la imagen está en e. ld e,d ; poner borde derecho en su lugar. ld d,c ; bit central va en d ld c,a ; y el borde izquierdo de nuevo en c. jr sprit0 ; hemos hecho el cambio para transferir a la pantalla. sprite ld a,(xcoord) ; dibuja el sprite (hl). ld (tmp1),a ; guardar vertical. call scadd ; calcular dirección de la pantalla. ld a,16 ; altura del sprite en pixeles. sprit1 ex af,af' ; guardar el contador de bucles. push de ; guardar dirección de pantalla. ld c,(hl) ; primer gráfico del sprite. inc hl ; incrementar el puntero de datos del sprite. ld d,(hl) ; siguiente bit de la imagen del sprite. inc hl ; apuntar a la siguiente fila de datos del sprite. ld (tmp0),hl ; guardar en tmp0 para más adelante. ld e,0 ; byte derecho en blanco por ahora. ld a,b ; b guarda la posición y. and 7 ; ¿estamos a caballo entre celdas de caracteres? jr z,sprit0 ; no estamos a caballo, no molesta al desplazamiento. cp 5 ; ¿necesitamos 5 o más desplazamientos a la derecha? jr nc,sprit7 ; sí, desplazar a la izquierda que es más rápido. and a ; hay, la bandera de acarreo se establece. sprit2 rr c ; rotar a la izquierda el byte derecho... rr d ; ...Hasta el byte medio... rr e ; ...la byte derecho. dec a ; un turno menos que hacer. jr nz,sprit2 ; repetir hasta que todos los turnos estén completos. sprit0 pop hl ; sacar la dirección de la pantalla en la pila. ld a,(hl) ; ya está lista. xor c ; fusionar con los datos de la imagen. ld (hl),a ; colocar en la pantalla. inc l ; siguiente celda de carácter por la derecha. ld a,(hl) ; ya estaba antes. xor d ; fusionarse con el centro de la imagen. ld (hl),a ; poner de nuevo en la pantalla. inc hl ; siguiente bit del área de la pantalla. ld a,(hl) ; lo que ya está allí. xor e ; borde derecho de los datos de imagen del sprite. ld (hl),a ; poner en pantalla. ld a,(tmp1) ; coordenada vertical temporal. inc a ; siguiente línea de abajo. ld (tmp1),a ; almacenar nueva posición. and 63 ; ¿nos movemos al siguiente tercio de pantalla? jr z,sprit4 ; sí, encontrar el próximo segmento. and 7 ; ¿entrando en celda de carácter siguiente? jr z,sprit5 ; Sí, encuentre siguiente fila. dec hl ; izquierda 2 bytes. dec l ; es está el límite a horcajadas de 256 bytes aquí. inc h ; siguiente fila de esta celda de carácter. sprit6 ex de,hl ; Dirección de pantalla en de. ld hl,(tmp0) ; restaurar la dirección del gráfico. ex af,af' ; restaurar el contador del bucle. dec a ; decrementarlo. jp nz,sprit1 ; no alcanzado el borde inferior del sprite, repetir. ret ; trabajo hecho. sprit4 ld de,30 ; el siguiente segmento es de 30 bytes. add hl,de ; añadir a la dirección de la pantalla. jp sprit6 ; repetir. sprit5 ld de,63774 ; menos 1762. add hl,de ; restar 1762 de la dirección física de la pantalla. jp sprit6 ; volver al bucle. scadd ld a,(xcoord) ; recuperar la coordenada vertical. ld e,a ; guardarla en e. ; Encuentra la línea dentro de la celda. and 7 ; línea 0-7 en el cuadro del carácter. add a,64 ; 64 * 256 = 16384 = inicio de la pantalla. ld d,a ; línea * 256. ; Encuentra en que tercio de pantalla estamos. ld a,e ; restaurar la vertical. and 192 ; segmento 0, 1 o 2 multiplicado por 64. rrca ; dividirlo por 8. rrca rrca ; segmento 0-2 multiplicado por 8. add a,d ; añadir a d da la dirección inicial del segmento. ld d,a ; Encuentra celda de carácter dentro del segmento. ld a,e ; 8 casillas de caracteres por segmento. rlca ; dividir por 8 y multiplicar por 32, rlca ; cálculo neto: multiplicar por 4. and 224 ; enmascarar los bits que no queremos. ld e,a ; cálculo de coordenada vertical hecho. ; Añadir el elemento horizontal. ld a,(ycoord) ; coordenada y. rrca ; sólo es necesario dividir por 8. rrca rrca and 31 ; cuadrados 0 - 31 por la pantalla. add a,e ; añadirse al total de la medida. ld e,a ; de = dirección de la pantalla. ret tmp0 defw 0 tmp1 defb 0 plgfx defb 127,254,255,255,254,127,252,127,248,127,248,127,254,127,254,127 defb 254,127,254,127,254,127,254,127,248,31,248,31,255,255,127,254 algfx defb 127,254,254,63,248,15,240,135,227,231,231,231,255,199,255,15 defb 252,31,248,127,241,255,227,255,224,7,224,7,255,255,127,254
Las mejores rutinas de movimiento de aliens usan una combinación de elementos
aleatorios y algoritmos de cazador-asesino. Para superar el problema en el listado
superior necesitamos un nuevo indicador adicional para indicar el estado actual
del enemigo o en su caso su dirección. Podemos mover el sprite a lo largo de
una dirección determinada hasta que sea posible y cambiar el curso vertical
u horizontal, con lo cual se selecciona una nueva dirección dependiendo de la
posición del jugador. Sin embargo, en caso de que no sea posible mover en la
dirección deseada iremos en la dirección opuesta en su lugar. Utilizando este
método un sprite puede encontrar su propio camino en la mayoría de los laberintos
sin colgarse con demasiada frecuencia. De hecho, para garantizar absolutamente
que el sprite no permanecerá atrapados podemos añadir un elemento al azar de
modo que cada cierto tiempo la nueva dirección es elegido de forma aleatoria
en lugar de la diferencia en las coordenadas x e y
Subiendo la dificultad por niveles
La ponderación aplicada a la decisión sobre los cambios de dirección determinarán
los niveles de inteligencia de los sprites. Si la nueva dirección tiene una
probabilidad del 90% de ser elegido de manera aleatoria y una probabilidad del
10% sobre la base de coordenadas, el alien paseará sin rumbo por un tiempo y
sólo se acerca cuando el jugador se mueve lento. Así, una decisión aleatoria
a veces puede ser la más adecuada cuando persigue al jugador. Un alien en una
pantalla más difícil podría tener una probabilidad del 60% de elegir una nueva
dirección al azar, y un 40% de posibilidades de elegir la dirección en base
a la posición relativa de jugador. Este alien hará un
seguimiento un poco más de cercano al jugador. Ajustando estos niveles porcentuales,
es posible determinar los niveles de dificultad a lo largo de un partido y asegurar
una transición suave desde el las más simples pantallas del inicio de la partida
a niveles finales diabólicamente difíciles.
No hay comentarios:
Publicar un comentario