Í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.
Movimiento sofisticado
Hasta ahora hemos movido sprites arriba, abajo, a la izquierda y a la derecha
por píxeles. Sin embargo, muchos juegos requieren una manipulación de sprites
más sofisticada. Los juegos de plataformas requieren manejar la gravedad, los
juegos de carreras top-down utilizan el movimiento de rotación, otros juegos utilizan
la inercia.
Tablas de salto y de inercia
La forma más sencilla de manejar la gravedad o la inercia es disponer de una
tabla de valores. Por ejemplo, los Egghead games hacen uso de una tabla
de saltos y mantienen un puntero a la posición actual. Dicha tabla puede tener
un aspecto como la siguiente.
; Tabla de saltos. ; Valores mayores de 128 son de subida, menores de 128 de bajada. ; Conforme el valor se aleja de 128 se aumenta la velocidad. jptr defw jtabu jtabu defb 250,251,252 defb 253,254,254 defb 255,255,255 defb 0,255,0 jtabd defb 1,0,1,1,1,2 defb 2,3,4,5,6,6 defb 6,6,128
Con el puntero almacenado en jptr, podríamos hacer algo como esto:
ld hl,(jptr) ; recuperar el puntero del salto. ld a,(hl) ; siguiente valor. cp 128 ; ¿alcanzado el final de la tabla? jr nz,skip ; no, estamos bien. dec hl ; atrás aumentamos la velocidad. ld a,(hl) ; recuperar la velocidad máxima. skip inc hl ; avanzar el puntero. ld (jptr),hl ; establecer la siguiente posición del puntero. ld hl,verpos ; posición vertical del jugador. add a,(hl) ; agregar la cantidad correspondiente. ld (hl),a ; Establecer la nueva posición del jugador.
Para iniciar un salto, debemos establecer jptr a jtabu. Para empezar
a caer, hay que configurarlo con jtabd.
Bien, esta descripción es muy simple. En la práctica, debemos utilizar el valor
de la tabla de saltos como un contador de bucles, mover al jugador arriba o
hacia abajo un píxel cada vez, comprobar colisiones con plataformas, paredes,
elementos mortales, etc. como siempre. También podríamos utilizar el elemento
final (128) para indicar que el jugador ha caído desde demasiado lejos, y establecer
un indicador para que la próxima vez que el jugador golpee contra algo sólido
pierda una vida. Considerando esto, ya tienes tu rutina.
Coordenadas fraccionarias
Si queremos manejar de forma más sofisticada la gravedad, la inercia o el movimiento
de rotación, necesitamos coordenadas fraccionarias. Hasta ahora, con la resolución
del Spectrum de 256x192 píxeles, sólo hemos tenido que utilizar un byte por
coordenadas. Si por el contrario utilizamos un par de registros de dos bytes,
el byte alto para la parte entera y el byte bajo para la fraccionaria, se abre
un nuevo mundo de posibilidades. Esto nos da 8 bits para cifras decimales binarias, lo
que permite movimientos muy precisos y sutiles. Con una coordenada en el par
hl, podemos configurar el desplazamiento en de, y unir los dos.
Al presentar nuestros sprites, simplemente usamos los bytes altos como nuestra
coordenadas X e Y para nuestro cálculo de la dirección de la pantalla, y desechamos
los bytes inferiores que contienen las fracciones. El efecto de sumar una fracción
a una coordenada no será visible cada cuadro, pero incluso la fracción más pequeña,
1/256, moverá lentamente un sprite con el tiempo.
Ahora podemos echar un vistazo a la gravedad. Esta es una fuerza constante,
en la práctica acelera un objeto hacia el suelo a 9,8 m/s2. Para
simularlo en un juego de Spectrum, haremos que nuestra coordenada vertical sea
una palabra de 16 bits. A continuación, establecemos una segunda palabra de
16 bits para la inercia. Cada cuadro añadimos una pequeña fracción a la inercia,
y a continuación añadimos la inercia a la posición vertical. Por ejemplo:
ld hl,(vermom) ; inercia. ld de,2 ; fracción pequeña, 1/128. add hl,de ; incrementar inercia. ld (vermom),hl ; guardar inercia. ld de,(verpos) ; posición vertical. add hl,de ; añadir inercia. ld (verpos),hl ; guardar nueva posición. ret verpos defw 0 ; posición vertical. vermom defw 0 ; inercia vertical.
A continuación, para mostrar nuestros sprites, simplemente tomamos el byte alto
de nuestra posición vertical, verpos+1, que nos dan el número
de píxeles desde la parte superior de la pantalla. Diferentes valores de
de variarán la fuerza de la gravedad, de hecho, incluso podemos
cambiar la dirección restando de con hl, o
añadiendo una distancia negativa (65536-distancia). Podemos aplicar lo misma
también a la coordenada y para mantener el sprite sujeto a la
inercia en todas direcciones. Así es como nos gustaría ir escribiendo un juego
de estilo similar a Thrust.
Movimiento rotatorio
Otro tema que podemos necesitar para juegos del estilo al Thrust,
carreras top-down, u otros en los que círculos o trigonometría básica están
involucrados es una tabla de senos y cosenos. Las matemáticas no son del agrado
de todo el mundo, y si tu trigonometría está un poco oxidada te sugiero leer
sobre senos y cosenos antes de continuar con el resto del capítulo.
En matemáticas, podemos encontrar la distancia x e y desde el centro de un círculo
dado el radio y el ángulo mediante el uso de senos y cosenos. Sin embargo, mientras
que en matemáticas un círculo se compone de 360 grados o lo que es lo mismo
de 2 PI radianes, es más conveniente para el programador de Spectrum representar
un ángulo como, por ejemplo, un valor de 8 bits de 0 a 255, o incluso usar menos
bits, dependiendo del número de posiciones que el sprite del jugador pueda tomar.
Podemos utilizar este valor para mirar su valor fraccionario de 16 bits para
el seno y el coseno de una tabla. Suponiendo que tenemos un ángulo de 8 bits
almacenado en el acumulador y deseamos encontrar el seno, simplemente accedemos
a la tabla de manera similar a la siguiente:
ld de,2 ; fracción pequeña - 1/128. ld l,a ; ángulo en el byte bajo. ld h,0 ; ponemos a cero byte alto. add hl,hl ; doble desplazamiento las entradas son de 16 bits. ld de,sintab ; dirección de tabla de senos. add hl,de ; añadir el desplazamiento de este ángulo. ld e,(hl) ; fracción del seno. inc hl ; apunto a la segunda mitad. ld d,(hl) ; parte entera. ret ; volver con el seno en DE.
Realmente el BASIC de Sinclair ya nos proporciona los valores que necesitamos,
con sus funciones SIN y COS. Usándolas, podemos
almacenar con POKE en la memoria RAM, o bien guardarlos en
cinta, o guardarlos en binario utilizando un emulador como SPIN.
Alternativamente, es posible que prefieras utilizar otro lenguaje de programación
en el PC para generar una tabla formateada de valores de senos, para importarlos
en tu archivo de origen o incluirlos como valores binarios. Para una tabla
de senos con 256 ángulos equidistantes se necesitaría un total de 512 bytes,
pero debemos que tener cuidado para convertir el número devuelto por
SIN en uno que nuestro juego reconozca. Multiplicando el seno por 256
nos dará nuestros valores positivos, pero cuando SIN devuelve
un resultado negativo, podrías tener que multiplicar el valor absoluto del seno
por 256, y luego restarlo de 65536 o poner el bit d7 del byte a uno para indicar
que el número debe ser restado en lugar de sumado a nuestras coordenadas. Con
una tabla de senos construida de esta manera no necesitamos una tabla separada
para los cosenos, solo hemos de añadir o restar 64, o un cuarto de vuelta, al
ángulo antes de obtener el valor en nuestra tabla. Para mover un sprite en un
ángulo de A, se añade el seno de A a una coordenadas, y el coseno de A a la
otra coordenada. Cambiando si sumamos o restamos un cuarto de vuelta para obtener
el coseno, y viendo que cuadrante utiliza senos y cual cosenos, podemos empezar
nuestro círculo en cualquiera de los 4 puntos cardinales y hacer que se mueva
en sentido horario o anti horario.
No hay comentarios:
Publicar un comentario