EL JUEGO
Nuevo intento de los Space Invaders para hacerse con el control de nuestro planeta !!!
¿Para que ir dando tumbos cuando puedes ir directo a tu objetivo?
Los invasores van bajando en columnas y con la ayuda de nuestro tanque debemos seleccionar el tipo de invasor y disparar.
Si el invasor está en esa columna será destruido y el resto retrocederán una posición.
Hay que evitar que lleguen a la zona de búnkeres o la victoria será suya.
Cada nivel lo componen 35 invasores y disponemos de 50 disparos para acabar con ellos.
Al superar un nivel llegamos a otro en el que los invasores avanzar más rápidamente.
El juego guarda la máxima puntuación alcanzada en una partida.
BLOQUES
He dividido el listado en 12 bloques:
- Declaración de las matrices.
- Control de nuestro tanque.
- Control de nuestro objetivo.
- Control de nuestro disparo.
- Mover invasor.
- Perder una vida.
- Pasar Nivel.
- Presentación.
- Mostrar pantalla de juego.
- Cargar UDG.
- Rutinas varias.
- Bloques de gráficos.
COMO FUNCIONA
Todo el programa ocupa 77 líneas.
A continuación indico el principio de las distintas partes del programa:
10 - Definimos las matrices del juego.
20 - Llamada a rutina para leer gráficos
50 - Mostrar la pantalla de presentación del juego.
100 – Comprobamos si se ha de mover horizontalmente nuestro tanque.
120 – Comprobamos si hemos de cambiar el invasor “objetivo” de la columna activa.
150 – Comprobamos si se ha de disparar. Se revisa si el invasor “objetivo” está en la columna, actuando en consecuencia.
160 – En caso de disparo revisamos si el invasor seleccionado está en la columna que apuntamos.
170 – En función del invasor asignamos la puntuación (F). Si es 9, es el UFO y cambiamos los puntos a 25.
175 – Reproducimos un sonido y actualizamos el marcador.
180 – Este bucle elimina el invasor de la columna.
200 – Si el contador de velocidad (W) no llega al valor deseado se incrementa en uno y se salta a la línea 290.
210 – Se selecciona una columna aleatoriamente y si quedan invasores por salir, salen, y si no se añade un espacio en la columna.
220 – Se comprueba si la columna ha llegado al búnker. En caso afirmativo se salta a la línea de perder vida (500).
250 – Incorpora el invasor en la matriz, desplazando el resto una posición.
260 – Refresca la columna de invasores seleccionada.
290 – Bucle que utiliza la variable de sistema “TIME” para que el juego tenga una velocidad constante.
300 – Si la variable de estado (E) se mantiene a 0 se vuelva a la línea 100.
400 – Rutina que se ejecuta cuando un invasor llega al búnker. Si nos queda alguna vida se actualizan los marcadores, sino muestra el mensaje de “GAME OVER” y después de una pausa volvemos a la pantalla de presentación.
500 – Rutina de nivel superado. Se incrementa en 1, pero si estamos en el 6 volvemos al 4.
2000 – Pantalla de presentación del juego.
2090 – Llamada a la rutina que vacía el buffer del teclado y espera que pulsemos una tecla para empezar el juego.
2100 – Muestra la parte de la derecha de la zona de juego.
2300 – Inicializamos las variables de Nivel (L), Vidas (V) y Puntuación (S).
2500 – Se actualiza el estado de las cinco columnas de invasores y del invasor “objetivo”.
2510 – Muestra la parte izquierda de la zona de juego, donde realmente se desarrolla la acción.
2550 – Se reinicializan ciertas variables del juego. Posición Tanque (P), Invasores pendientes (O), Munición (B), Estado (E).
3000 – Inicio de la rutina que redefine los caracteres. Reducimos el RAMTOP en 1KB y ponemos un mensaje de copyright.
3005 – Ocultamos el cursor y cambiamos los colores de letra, fondo y borde.
3010 – Copiamos parte de los caracteres desde la ROM a la RAM, y modificamos la dirección que indica donde están.
3015 – Completamos la redefinición leyendo los datos desde nuestras DATA. Redefinimos las minúsculas.
3020 – Asignamos los gráficos de los distintos elementos a variables.
4000 – Rutina de Pausa, pasando en valor en (I).
4100 – Rutina para imprimir los valores de “REST” y Munición “AMMO”. Posición vertical en (Y) y valor en (N).
4200 – Rutina para imprimir la puntuación y el récord en formato de 4 cifras. Posición horizontal en (Y) y valor en (N).
4300 – Rutina que espera la pulsación de una tecla, y la guarda en (K). A continuación borra el valor del buffer de teclado.
4400 – Rutina que reproduce un sonido de una frecuencia (N) durante un breve periodo de tiempo.
4500 – Rutina para hacer un borrado parcial de la pantalla.
9000 – DATA con los distintos gráficos del juego.
EL LISTADO
Código: Seleccionar todo
10 DIM T$(2),G$(20),C$(3),I$(40),T(5),A$(1),S$(4),X$(4)
20 GOSUB 3000
50 GOSUB 2000
' Controlar nuestro tanque
100 K=PEEK(764):IF K=255 THEN 200
105 POKE 764,255:IF K=28 THEN GRAPHICS 1:GRAPHICS 0:POKE 752,0:END
110 IF K=23 THEN IF P>1 THEN P=P-1:POSITION P*3+2,21:PRINT T$;" "
115 IF K=22 THEN IF P<5 THEN P=P-+:POSITION P*3-1,21:PRINT " ";T$
120 IF K<>7 THEN 150
130 T(P)=T(P)+1:IF T(P)>9 THEN T(P)=1
140 POSITION P*3+2,19:F=T(P)*2+1:PRINT G$(F,F+1)
' Disparo contra un invasor
150 IF K<>12 OR B=0 THEN 200
155 N=121:GOSUB 4400:B=B-1:Y=3:N=B:GOSUB 4100:A$=STR$(T(P)):I=0:Z=(P-1)*8
160 FOR F=8 TO 1 STEP -1:IF A$=I$(Z+F,Z+F) THEN I=F:F=0
165 NEXT F:IF I=0 THEN 200
170 F=T(P):IF F=9 THEN F=25
175 S=S+F:Y=5:N=S:GOSUB 4200:M=M+1:IF M=35 THEN 500
180 FOR F=I TO 7:I$(Z+F,Z+F)=I$(Z+F+1,Z+F+1):NEXT F
190 F=P:GOTO 260
' Movimiento de los invasores
200 W=W+1:IF W<40-L*5 THEN 300
210 N=81:GOSUB 4400:W=0:F=INT(RND(0)*5)+1:I=0:IF O>0 THEN I=INT(RND(0)*9)+1:O=O-1:Y=5:N=O:GOSUB 4100
220 E=0:Z=F*8-1:IF I$(F,F)<>"0" THEN E=1
250 Z=(F-1)*8+8:FOR J=8 TO 2 STEP -1:I$(Z,Z)=I$(Z-1,Z-1):Z=Z-1:NEXT J:I$(Z,Z)=STR$(I)
260 Z=F*3+2:I=(F-1)*8:N=3:FOR J=1 TO 7+E:POSITION Z,N:F=VAL(I$(I+J,I+J))*2+1:PRINT G$(F,F+1):N=N+2:NEXT J
300 IF E=0 THEN 100
' El invasor llega al bunker. Perdemos vida.
400 FOR Z=81 TO 121 STEP 5:N=Z:GOSUB 4400:NEXT Z:I=750:GOSUB 4000:GOSUB 4310:IF V=1 THEN 450
410 V=V-1:POSITION 3,23:PRINT STR$(V);" ";
420 IF V>1 THEN FOR F=2 TO V:PRINT T$;:NEXT F
430 PRINT " ";:GOSUB 2500:GOTO 100
450 POSITION 11,23:PRINT "<GAME OVER>";:I=1500:GOSUB 4000:IF S>R THEN R=S
460 GOSUB 4500:GOTO 50
' Pasamos nivel
500 FOR Z=1 TO 3:N=81:GOSUB 4400:N=91:GOSUB 4400:NEXT Z:I=750:GOSUB 4000:L=L+1:IF L=6 THEN L=4
510 POSITION 33,9:PRINT L:GOSUB 2500:GOSUB 4310:GOTO 100
' Pantalla de presentación
2000 POSITION 2,0:PRINT " SCORE<1> HI-SCORE SCORE<2>"
2020 Y=5:N=S:GOSUB 4200:Y=18:N=R:GOSUB 4200
2030 POSITION 18,4:PRINT "PLAY":POSITION 13,6:PRINT "SPACE INVASION"
2040 POSITION 9,9:PRINT "*SCORE ADVANCE TABLE*":PRINT
2050 FOR F=1 TO 4:Z=F*2:PRINT " ";G$(Z+1,Z+2);"=";STR$(F);" POINTS * ";
2060 PRINT G$(Z+9,Z+10);"=";STR$(F+4);" POINTS":PRINT:NEXT F
2070 PRINT " ";G$(19,20);"=25 POINTS"
2075 PRINT:PRINT:PRINT " __________________________________"
2080 POSITION 3,23:PRINT "3 ";T$;T$;" ";:POSITION 29,23:PRINT "CREDIT 00";
2085 GOSUB 4310:GOSUB 4300:GOSUB 4500
2100 POSITION 24,3:PRINT "* AMMO : 50":POSITION 24,5:PRINT "* REST : 35":POSITION 24,9: PRINT "* LEVEL: 1"
2110 POSITION 24,14:PRINT "* KEYS:":POSITION 26,16:PRINT " Z : LEFT":POSITION 26,17:PRINT " X : RIGHT"
2120 POSITION 26,18:PRINT "RTN : FIRE":POSITION 26,19:PRINT " * : CHANGE"
2300 L=1:V=3:S=0:Y=5:N=S:GOSUB 4200
2500 FOR F=1 TO 5:T(F)=1:NEXT F:I$="0000000000000000000000000000000000000000"
2510 FOR F=3 TO 16:POSITION 3,F:PRINT "w w":NEXT F
2520 PRINT " "+C$+C$+C$+C$+C$:POSITION 5,21:PRINT " ";T$;" "
2540 POSITION 3,19:PRINT "> ab ab ab ab ab <"
2550 P=3:W=0:E=0:O=35:M=0:B=50:Y=3:N=B:GOSUB 4100:Y=5:N=O:GOSUB 4100
2560 RETURN
' Rutina definir UDG (gráficos de usuario)
3000 POKE 106,PEEK(106)-5:GRAPHICS 0:POSITION 9,11:PRINT "(C) SCAINET SOFT, 2013 ";
3005 POKE 710,0:POKE 709,14:POKE 712,1:POKE 752,1
3010 UDG=(PEEK(106)+1)*256:FOR I=0 TO 775:POKE UDG+I,PEEK(57344+I):NEXT I:POKE 756,UDG/256
3015 FOR F=UDG+776 TO UDG+959:READ I:POKE F,I:NEXT F
3020 T$="st":G$=" abcdefghijklmnopqr":C$="uv ":R=0
3030 RETURN
' Rutinas varias (4000-Pausa, 4100-Valores, 4200-Marcadores, 4300-PressKey, 4400-Sound, 4500-Cls)
4000 FOR F=1 TO I:NEXT F:RETURN
4100 POSITION 33,Y:PRINT STR$(N);" ";:RETURN
4200 X$=STR$(N):S$="0000":N=LEN(X$):S$(5-N,4)=X$:POSITION Y,1:PRINT S$:RETURN
4300 K=PEEK(764):IF K=255 THEN 4300
4310 POKE 764,255:RETURN
4400 SOUND 0,N,10,15:I=10:GOSUB 4000:SOUND 0,0,0,0:RETURN
4500 FOR F=3 TO 21:POSITION 2,F:PRINT " ":NEXT F
' Datos de los UDG
9000 DATA 1,3,7,16,15,5,8,4,128,192,224,176,240,160,16,32
9010 DATA 8,4,15,27,63,63,40,6,32,64,224,176,248,248,40,192
9020 DATA 3,31,63,57,63,14,25,12,192,248,252,156,252,112,152,48
9030 DATA 3,7,13,15,6,3,4,2,192,224,176,240,96,192,32,64
9040 DATA 1,3,7,13,15,2,5,10,128,192,224,176,240,64,160,80
9050 DATA 8,36,47,59,63,31,8,16,32,72,232,184,248,240,32,16
9060 DATA 3,31,63,57,63,6,13,48,192,248,252,156,252,96,176,12
9070 DATA 3,7,13,15,6,3,2,4,192,224,176,240,96,192,64,32
9080 DATA 0,7,31,63,109,255,57,16,0,224,248,252,182,255,156,8
9090 DATA 1,3,3,127,255,255,255,255,0,128,128,252,254,254,254,254
9100 DATA 31,63,127,255,255,252,248,248,248,252,254,255,255,63,31,31
9110 DATA 60,36,60,24,24,60,36,60
APUNTES FINALES
Este es el primer juego que hago para el sistema ATARI de 8 bits, y me ha servido para comprobar las limitaciones de su BASIC, bastante estándar pero con importantes limitaciones y engorros.A diferencia de las versiones ELECTRON y MSX, este sistema no tiene búfer de teclado. Bueno, ni siquiera tiene instrucciones para leerlo y se ha de hacer con un PEEK(764). Esto hace que la pulsación de las teclas no sea muy fiable a la hora de cambiar el invasor objetivo. Una vez leído el teclado hay que hacer “POKE 764,255” para borrar la tecla pulsada.Los gráficos del juegos son de 16x8, pero se han conseguido redefiniendo los caracteres de 8x8, y que no usan el estándar ASCII. Este BASIC no tiene comandos para redefinir caracteres y se ha de hacer mediante un proceso un tanto especial. El proceso consiste en rebajar el RAMTOP en 1KB para guardar nuestro propio juego, copiar mediante un bucle los datos desde la ROM a la RAM que hemos reservado, poner nuestros propios caracteres mediante un READ y un POKE en las posiciones
correspondientes y indicarle al sistema la nueva dirección del juego de caracteres.
Una vez se conoce el método no es complicado hacerlo, pero la ejecución lleva un tiempo porque este sistema no es especialmente rápido con los bucles.
Por otro lado, si ejecutamos la rutina dos veces funciona mal, con lo que antes de volver a ejecutar es necesario pulsar el botón “RESET”, que no borra el programa pero cambia ciertas variables del sistema.
Respecto a las variables, este sistema solo dispone de un único sistema de variables numéricas. En otros sistemas, el uso de variables enteras acelera los cálculos.
Me ha sorprendido la inexistencia de matrices alfanuméricas, pero si numéricas. Con el comando DIM indicas el tamaño total de una variable alfanumérica, pero usando el índice solo accedes a una posición concreta. Para usarlo como matrices te tienes que montar tú mismo todo el sistema de organización, haciendo, por ejemplo, que todos los valores tengan el mismo tamaño. Las matrices numéricas, de una única dimensión, funcionan sin problemas.
Tampoco dispone de instrucciones tipo LEFT, RIGHT, MID, TRIM o USING. Así, al adaptar el juego he tenido que solucionarlo de otras formas. Pero como se ve, se ha podido hacer.
El bucle principal de desarrollo va de la línea 100 hasta la 300, 21 líneas en total. Aquí se controla el tanque, el disparo y el movimiento de los invasores.
He eliminado todos los cálculos posibles y todos los AND/OR posibles. Uso un IF y si no se cumple ya no tengo que comprobar más valores.
El rendimiento del juego es bastante estable excepto cuando se redibujar una columna de invasores. No he podido usar la variable de sistema TIME, que se incrementa en 1 varias veces por segundo y es útil para fijar la velocidad de un juego.
Si no fuera por el tema del teclado, el juego sería casi idéntico a la versión del ELECTRON ya que los dos funcionan a 40 columnas. Por ello y el TIME, ésta es la versión más floja de las tres que he realizado.
Para el tema de sonido, en el que sigo siendo un perfecto inútil, he usado el comando SOUND mediante la rutina de la línea 4500. Simplemente paso un valor como parámetro y se reproduce un sonido. El sistema es bastante más potente del uso que le he dado yo.
Respecto a la grabación del programa, se hace con un simple CSAVE, sin indicar nombre. Mientras graba o carga va sonando por el altavoz, y según he comprobado va grabando o leyendo bloques de 128 bytes. Es lento pero aparentemente seguro.
Como apunte final, me ha gustado programar este juego en el ATARI 800XL. Su teclado es fiable y muy retro. Hacía tiempo que deseaba hacer algo con él, y creo que este juego es perfecto para un primer contacto. Para el próximo proyecto haré uso de SPRITES.
Para terminar, todo este desarrollo se ha programado en un ATARI 800XL auténtico.
Os invito a probarlo