27 septiembre 2014

Un mini-juego en BASIC de Amstrad CPC (4: comentarios, azar, colisiones)

16. Comentarios

En general, nuestro programa será más legible si usamos variables que si empleamos "números mágicos" cuyo significado pueda no ser evidente para otro programador... o incluso para nosotros mismos poco después. Por eso estamos haciendo cosas como "IF INKEY(izqda) <> -1 THEN x=x-1" en vez de "IF INKEY(8) <> -1 THEN x=x-1".
Una segunda medida para hacerlo más legible es usar "comentarios", líneas de programa que no contienen órdenes, sino textos aclaratorios para el programador, que nos ayudan a explicar la misión de cada bloque de programa. Comienzan con la palabra REM o con un apóstrofe ('):
1 ' Ejemplo de juego en BASIC de CPC
15 arriba=0: abajo=2: derecha=1: izqda=8: salir=63: ' Teclas
Así, podemos añadir para añadir comentarios que hagan que las partes del bucle de juego sean más fáciles de localizar:
17 ' ----- Bucle de juego -----
25 ' -- Borrar personaje de su posicion anterior --
41 ' -- Comprobar teclas
51 ' -- Mover enemigos, entorno --
52 ' (Nada aun)
53 ' -- Colisiones, perder vidas, etc --
54 ' (Nada aun)
55 ' -- Dibujar en nueva posicion --
86 ' -- Pausa hasta el siguiente "fotograma" del juego
De modo que ahora nuestro programa quedaría:
1 ' Ejemplo de juego en BASIC de CPC
5 MODE 1: INK 0,0: INK 1,20
10 x=10: y=5: terminado=0
15 arriba=0: abajo=2: derecha=1: izqda=8: salir=63: ' Teclas
17 ' ----- Bucle de juego -----
20 WHILE terminado = 0
25 ' -- Borrar personaje de su posicion anterior --
30 LOCATE x,y
40 PRINT " "
41 ' -- Comprobar teclas --
42 IF INKEY(arriba) <> -1 THEN y=y-1
44 IF INKEY(abajo) <> -1 THEN y=y+1
46 IF INKEY(derecha) <> -1 THEN x=x+1
48 IF INKEY(izqda) <> -1 THEN x=x-1
50 IF INKEY(salir) <> -1 THEN terminado=1
51 ' -- Mover enemigos, entorno --
52 ' (Nada aun)
53 ' -- Colisiones, perder vidas, etc --
54 ' (Nada aun)
55 ' -- Dibujar en nueva posicion --
60 LOCATE x,y
70 PRINT CHR$(248)
86 ' -- Pausa hasta el siguiente "fotograma" del juego --
90 FOR pausa = 1 TO 200: NEXT pausa
100 WEND

17. No salir de la pantalla

Para no salir de la pantalla, deberemos comprobar si el valor de cada X y de cada Y es válido. Como estos valores cambiar cuando se pulsa una tecla, las comprobaciones deberían estar en las mismas líneas de programa que comprueban las pulsaciones de teclas:
42 IF INKEY(arriba) <> -1 AND y > 1 THEN y=y-1
44 IF INKEY(abajo) <> -1 AND y < 25 THEN y=y+1
46 IF INKEY(derecha) <> -1 AND x < 40 THEN x=x+1
48 IF INKEY(izqda) <> -1 AND x > 1 THEN x=x-1
Los límites de 40 a lo ancho y de 25 a lo alto vienen impuestos por la pantalla de un Amstrad CPC en su modo de trabajo normal (el "modo 1").

18. Tres obstáculos con coordenadas al azar

Igual que tenemos nuestro personaje con coordenadas X e Y, podríamos crear varios obstáculos con sus propias coordenadas X e Y:
12 xo1=10: yo1=5: xo2=30: yo2=20: xo3=21: yo3=16
Y dibujarlos en la parte del bucle de juego que se encarga de dibujar todos los elementos:
56 LOCATE xo1,yo1:PRINT"x"
57 LOCATE xo2,yo2:PRINT"x"
58 LOCATE xo3,yo3:PRINT"x"
Si queremos que sus coordenadas no estén prefijadas, sino que se generen al azar, podemos usar la función RND, que nos permite obtener un número al azar entre 0 y 1. Normalmente preferiremos que ocupen casi toda la pantalla, por ejemplo con valores entre 2 y 38, para lo que podemos multiplicar ese número por 36 y sumarle 2, así: RND*36+2. Realmente, tendrán que ser números enteros (sin cifras decimales), para que sean coordenadas válidas de pantalla, así que deberíamos quedarnos con la parte entera de ese número obtenido al azar: INT(RND*36+2). De igual modo, los valores de Y los podríamos obtener con INT(RND*22+2):
12 xo1=INT(RND*36+2): yo1=INT(RND*22+2): xo2=INT(RND*36+2): yo2=INT(RND*22+2): xo3=INT(RND*36+2): yo3=INT(RND*22+2)
Los valores que obtenemos con RND no son totalmente al azar, sino que parten de una cierta "semilla". Para que dos sesiones de juego no sean exactamente igual, sería conveniente tomar esa semilla a partir del reloj interno del ordenador (del tiempo que lleva encendido), con:
7 RANDOMIZE TIME

19. Comprobación de colisiones

En modo texto, en que cada símbolo ocupa por completo una casilla de pantalla, la comprobación de colisiones es sencilla: si coinciden la X y la Y de nuestro personaje con la de un obstáculo, entonces han chocado, lo que podríamos usar para indicar el fin de la partida:
54 IF x=xo1 AND y=yo1 THEN terminado = 1
Como tenemos tres obstáculos, la condición real es un poco más larga:
54 IF (x=xo1 AND y=yo1) OR (x=xo2 AND y=yo2) OR (x=xo3 AND y=yo3) THEN terminado = 1
De modo que nuestro programa, todavía sin renumerar, debería ser algo como:
1 ' Ejemplo de juego en BASIC de CPC
5 MODE 1: INK 0,0: INK 1,20
7 RANDOMIZE TIME
10 x=10: y=5: terminado=0
12 xo1=INT(RND*36+2): yo1=INT(RND*22+2): xo2=INT(RND*36+2): yo2=INT(RND*22+2): xo3=INT(RND*36+2): yo3=INT(RND*22+2)
15 arriba=0: abajo=2: derecha=1: izqda=8: salir=63: ' Teclas
17 ' ----- Bucle de juego -----
20 WHILE terminado = 0
25 ' -- Borrar personaje de su posicion anterior --
30 LOCATE x,y
40 PRINT " "
41 ' -- Comprobar teclas --
42 IF INKEY(arriba) <> -1 AND y > 1 THEN y=y-1
44 IF INKEY(abajo) <> -1 AND y < 25 THEN y=y+1
46 IF INKEY(derecha) <> -1 AND x < 40 THEN x=x+1
48 IF INKEY(izqda) <> -1 AND x > 1 THEN x=x-1
50 IF INKEY(salir) <> -1 THEN terminado=1
51 ' -- Mover enemigos, entorno --
52 ' (Nada aun)
53 ' -- Colisiones, perder vidas, etc --
54 IF (x=xo1 AND y=yo1) OR (x=xo2 AND y=yo2) OR (x=xo3 AND y=yo3) THEN terminado = 1
55 ' -- Dibujar en nueva posicion --
56 LOCATE xo1,yo1:PRINT"x"
57 LOCATE xo2,yo2:PRINT"x"
58 LOCATE xo3,yo3:PRINT"x"
60 LOCATE x,y
70 PRINT CHR$(248)
86 ' -- Pausa hasta el siguiente "fotograma" del juego --
90 FOR pausa = 1 TO 200: NEXT pausa
100 WEND
Hay demasiado poco espacio entre algunas línea, y vamos a necesitarlo para poder añadir un enemigo que se mueva, así que vamos a renumerar.
RENUM

20. Un enemigo móvil

Si queremos uno o varios enemigos, la estructura que repetiríamos es básicamente la misma que para los obstáculos, con una diferencia: podemos hacer que se muevan, bien sea persiguiéndonos o de lado a lado. Hacer que nos sigan puede ser muy fácil, si no hay "paredes" ni "obstáculos" o bastante complicado en caso de que los haya y el enemigo deba "esquivarlos". Vamos a ver el caso de que se muevan de lado a lado, que es razonablemente sencillo.
Bastará con que, en la parte de nuestro programa destinada a "Mover enemigos y entorno", cambiemos su coordenada X, o su Y, o ambas. Si queremos que "reboten" a un lado y a otro, lo podemos hacer sumando un cierto "incremento" a su X. Este incremento será positivo (+1) para que se mueva a la derecha y negativo (-1) para que se mueva a la izquierda, así que basta con cambiarle el signo cada vez que llegue a un extremo, como ya hicimos con los primeros movimientos de nuestro personaje.
Empezaremos por preparar las variables que representarán su posición inicial y su velocidad:
52 xE1=15: yE1=10: velocE1=1
En el bloque de "mover enemigo" será donde incrementemos la posición tanto como indique la velocidad y comprobemos si debe dar la vuelta:
185 xE1 = xE1 + velocE1
190 IF xE1=1 OR xE1=40 THEN velocE1 = -velocE1
Y habrá que dibujar el enemigo en la parte que dibuja los elementos en pantalla:
272 LOCATE xE1,yE1:PRINT"e"
También tendremos que borrarlo al principio de cada pasada por el bucle de juego:
90 ' -- Borrar personaje y enemigo de su posicion anterior --
92 LOCATE xE1,yE1
94 PRINT " "
Y habrá que comprobar colisiones con él:
212 IF x=xE1 AND y=yE1 THEN terminado = 1
Nuestro programa ahora debería ser algo como
10 ' Ejemplo de juego en BASIC de CPC
20 MODE 1: INK 0,0: INK 1,20
30 RANDOMIZE TIME
40 x=10: y=5: terminado=0
50 xo1=INT(RND*36+2): yo1=INT(RND*22+2): xo2=INT(RND*36+2): yo2=INT(RND*22+2): xo3=INT(RND*36+2): yo3=INT(RND*22+2)
52 xE1=15: yE1=10: velocE1=1
60 arriba=0: abajo=2: derecha=1: izqda=8: salir=63: ' Teclas
70 ' ----- Bucle de juego -----
80 WHILE terminado = 0
90 ' -- Borrar personaje y enemigo de su posicion anterior --
92 LOCATE xE1,yE1
94 PRINT " "
100 LOCATE x,y
110 PRINT " "
120 ' -- Comprobar teclas --
130 IF INKEY(arriba) <> -1 AND y > 1 THEN y=y-1
140 IF INKEY(abajo) <> -1 AND y < 25 THEN y=y+1
150 IF INKEY(derecha) <> -1 AND x < 40 THEN x=x+1
160 IF INKEY(izqda) <> -1 AND x > 1 THEN x=x-1
170 IF INKEY(salir) <> -1 THEN terminado=1
180 ' -- Mover enemigos, entorno --
185 xE1 = xE1 + velocE1
190 IF xE1=1 OR xE1=40 THEN velocE1 = -velocE1
200 ' -- Colisiones, perder vidas, etc --
210 IF (x=xo1 AND y=yo1) OR (x=xo2 AND y=yo2) OR (x=xo3 AND y=yo3) THEN terminado = 1
212 IF x=xE1 AND y=yE1 THEN terminado = 1
220 ' -- Dibujar en nueva posicion --
230 LOCATE xo1,yo1:PRINT"x"
240 LOCATE xo2,yo2:PRINT"x"
250 LOCATE xo3,yo3:PRINT"x"
260 LOCATE x,y
270 PRINT CHR$(248)
272 LOCATE xE1,yE1:PRINT"e"
280 ' -- Pausa hasta el siguiente "fotograma" del juego --
290 FOR pausa = 1 TO 200: NEXT pausa
300 WEND