Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
cursos:z88dk:sprites2 [10-08-2007 09:53]
sromero
cursos:z88dk:sprites2 [24-02-2020 20:24] (actual)
falvarez
Línea 1: Línea 1:
-¡MIS SPRITES SE MUEVEN!+Publicado originalmente en [[https://magazinezx.speccy.org/12/z88dk.html|MagazineZX número 12]] (septiembre 2005) 
 + 
 +====== Curso de Z88DK: Sprites (II) ====== 
 + 
 +===== ¡Mis Sprites se mueven=====
  
 En el artículo anterior aprendimos cómo definir sprites y dibujarlos en pantalla utilizando la librería z88dk sin recurrir a ninguna otra herramienta externa (a menos que quisiéramos dibujar los sprites utilizando algún programa como los mostrados en el número anterior). En esta entrega vamos a ir un poco más allá y vamos a permitir que el usuario pueda mover algún sprite por la pantalla, y que este sprite pueda interaccionar con otros elementos, colisionando con ellos. En el artículo anterior aprendimos cómo definir sprites y dibujarlos en pantalla utilizando la librería z88dk sin recurrir a ninguna otra herramienta externa (a menos que quisiéramos dibujar los sprites utilizando algún programa como los mostrados en el número anterior). En esta entrega vamos a ir un poco más allá y vamos a permitir que el usuario pueda mover algún sprite por la pantalla, y que este sprite pueda interaccionar con otros elementos, colisionando con ellos.
  
-¿Qué vamos a hacer?+===== ¿Qué vamos a hacer? =====
  
-Para aprender los conceptos que necesitamos vamos a programar lo que podría ser el código inicial de un juego de carreras de coches, tipo Super Sprint, en los que desde una vista cenital podemos ver el circuito y los distintos participantes. Crearemos un coche utilizando diversos sprites, según hacia donde se esté moviendo el mismo, y crearemos una pista por donde el coche podrá moverse. Evidentemente, esta pista marcará el límite de movimiento de nuestro bólido, por lo que deberemos implementar también algún mecanismo para colisiones, de tal manera que al vehículo le pase algo al contactar con los límites del circuito. Sin más, empecemos. 
  
-Definiendo los sprites+Para aprender los conceptos que necesitamos vamos a programar lo que podría ser el código inicial de un juego de carreras de coches, tipo //Super Sprint//, en los que desde una vista cenital podemos ver el circuito y los distintos participantes. Crearemos un coche utilizando diversos sprites, según hacia donde se esté moviendo el mismo, y crearemos una pista por donde el coche podrá moverse. Evidentemente, esta pista marcará el límite de movimiento de nuestro bólido, por lo que deberemos implementar también algún mecanismo para colisiones, de tal manera que al vehículo le pase algo al contactar con los límites del circuito. Sin más, empecemos. 
 + 
 +===== Definiendo los sprites =====
  
 Hasta ahora hemos visto como crear sprites de un tamaño límite de 8x8. Sin embargo, por mucho que lo intentemos, va a ser un poco difícil crear un sprite que represente algo aproximado a un coche con una rejilla tan pequeña. Si nos fijamos en la siguiente imagen, observaremos que tanto para representar al coche en todas las posibles orientaciones, así como los neumáticos que van a formar parte de los límites de la pista, necesitamos utilizar sprites de 10x10. ¿Cómo creamos sprites mayores de 8x8 usando z88dk? Hasta ahora hemos visto como crear sprites de un tamaño límite de 8x8. Sin embargo, por mucho que lo intentemos, va a ser un poco difícil crear un sprite que represente algo aproximado a un coche con una rejilla tan pequeña. Si nos fijamos en la siguiente imagen, observaremos que tanto para representar al coche en todas las posibles orientaciones, así como los neumáticos que van a formar parte de los límites de la pista, necesitamos utilizar sprites de 10x10. ¿Cómo creamos sprites mayores de 8x8 usando z88dk?
-Figura 1: Los sprites que vamos a utilizar en nuestro juego. Representan todas las posibles orientaciones del coche, excepto el último de todos, que representa un neumático. Todos ellos tienen un tamaño de 10x10 + 
-Figura 1Los sprites que vamos a utilizar en nuestro juegoRepresentan todas las posibles orientaciones del coche, excepto el último de todos, que representa un neumático. Todos ellos tienen un tamaño de 10x10+ Veamos los sprites que vamos a utilizar en nuestro juego. Representan todas las posibles orientaciones del coche, excepto el último de todos, que representa un neumático. Todos ellos tienen un tamaño de 10x10 
 + 
 +{{ cursos:z88dk:z88dk7_1.png |Sprites del juego}}
  
 Según vimos anteriormente, un sprite se definía en z88dk como un array de char del tipo: Según vimos anteriormente, un sprite se definía en z88dk como un array de char del tipo:
  
 +<code c>
 char sprite0[] = { 8, 8, 0x18 , 0x24 , 0x42 , 0x81 , 0xA5 , 0x81 , 0x7E , 0x00 }; char sprite0[] = { 8, 8, 0x18 , 0x24 , 0x42 , 0x81 , 0xA5 , 0x81 , 0x7E , 0x00 };
 +</code>
  
 donde los dos primeros valores indicaban la altura y la anchura, respectivamente, y los siguientes valores representaban a cada una de las filas del sprite. Si cada columna tenía asignado un valor hexadecimal (estos valores eran, para una anchura de 8, y de derecha a izquierda, los siguientes: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80), el valor para cada fila era la suma de los valores hexadecimales para los que el pixel correspondiente valía 1. donde los dos primeros valores indicaban la altura y la anchura, respectivamente, y los siguientes valores representaban a cada una de las filas del sprite. Si cada columna tenía asignado un valor hexadecimal (estos valores eran, para una anchura de 8, y de derecha a izquierda, los siguientes: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80), el valor para cada fila era la suma de los valores hexadecimales para los que el pixel correspondiente valía 1.
Línea 21: Línea 30:
 Para sprites de más de anchura 8, y hata una anchura de 16, en ese array de caracteres cada fila se va a representar por un par de números hexadecimales. El primer valor de esa pareja se va a corresponder con los 8 primeros píxeles empezando por la izquierda de la columna, y el segundo con el resto. Para sprites de más de anchura 8, y hata una anchura de 16, en ese array de caracteres cada fila se va a representar por un par de números hexadecimales. El primer valor de esa pareja se va a corresponder con los 8 primeros píxeles empezando por la izquierda de la columna, y el segundo con el resto.
  
-En el caso de los sprites que vemos en la figura anterior, donde cada columna tiene una anchura de diez píxeles, cada fila se representaría también por una pareja de valores hexadecimales. Con la primera pareja codificamos los ocho píxeles de la izquierda, de la misma forma en que lo hacemos con sprites de anchura hasta 8, y con la segunda pareja codificamos los otros dos pixeles que quedan a la derecha (siendo el valor 0x80 el correspondiente al primero y el valor 0x40 el correspondiente al segundo. Si hubiese más de dos, seguiríamos asignando los valores 0x20, 0x10, 0x08, 0x04 y etc. a los siguientes). En la siguiente imagen vemos un ejemplo+En el caso de los sprites que vemos en la figura anterior, donde cada columna tiene una anchura de diez píxeles, cada fila se representaría también por una pareja de valores hexadecimales. Con la primera pareja codificamos los ocho píxeles de la izquierda, de la misma forma en que lo hacemos con sprites de anchura hasta 8, y con la segunda pareja codificamos los otros dos pixeles que quedan a la derecha (siendo el valor 0x80 el correspondiente al primero y el valor 0x40 el correspondiente al segundo. Si hubiese más de dos, seguiríamos asignando los valores 0x20, 0x10, 0x08, 0x04 y etc. a los siguientes). En la siguiente imagen vemos un ejemplo del cálculo de los valores hexadecimales correspondientes a cada una de las filas del sprite 0: 
-Figura 2: cálculo de los valores hexadecimales correspondientes a cada una de las filas del sprite 0 + 
-Figura 2cálculo de los valores hexadecimales correspondientes a cada una de las filas del sprite 0+{{ cursos:z88dk:z88dk7_2.png |Filas del Sprite 0}}
  
 Podríamos ser malvados y dejar como ejercicio al lector que calcule cómo se codificarían el resto de sprites, pero vamos a ahorrarle el mal trago. A continuación incluímos el código que codifica todos los sprites anteriores, que deberemos introducir dentro de un archivo de cabecera llamado coches.h: Podríamos ser malvados y dejar como ejercicio al lector que calcule cómo se codificarían el resto de sprites, pero vamos a ahorrarle el mal trago. A continuación incluímos el código que codifica todos los sprites anteriores, que deberemos introducir dentro de un archivo de cabecera llamado coches.h:
  
 +<code c>
 char sprite0[] = { 10, 10, 0x00 , 0x00 , 0x73 , 0x80 , 0xFF , 0x40 , 0xB9 , char sprite0[] = { 10, 10, 0x00 , 0x00 , 0x73 , 0x80 , 0xFF , 0x40 , 0xB9 ,
      0xC0 , 0xB9 , 0xC0 , 0xB9 , 0xC0 , 0xB9 , 0xC0 , 0xFF , 0x40 , 0x73 ,      0xC0 , 0xB9 , 0xC0 , 0xB9 , 0xC0 , 0xB9 , 0xC0 , 0xFF , 0x40 , 0x73 ,
Línea 54: Línea 64:
      0xC0 , 0xE1 , 0xC0 , 0xE1 , 0xC0 , 0xF3 , 0xC0 , 0x7F , 0x80 , 0x7F ,      0xC0 , 0xE1 , 0xC0 , 0xE1 , 0xC0 , 0xF3 , 0xC0 , 0x7F , 0x80 , 0x7F ,
      0x80 , 0x1E , 0x00  };      0x80 , 0x1E , 0x00  };
 +</code>
  
 Y a continuación indicamos el código de un pequeño programa, que llamaremos coches.c, que lo único que hará será mostrar por pantalla todos los sprites para comprobar que quedan bonitos. Para ello hacemos uso de la función putsprite, cuya sintaxis se explicó en el artículo anterior: Y a continuación indicamos el código de un pequeño programa, que llamaremos coches.c, que lo único que hará será mostrar por pantalla todos los sprites para comprobar que quedan bonitos. Para ello hacemos uso de la función putsprite, cuya sintaxis se explicó en el artículo anterior:
  
 +<code c>
 #include "games.h" #include "games.h"
 #include "coches.h" #include "coches.h"
Línea 72: Línea 84:
         putsprite(spr_or,1,81,sprite8);         putsprite(spr_or,1,81,sprite8);
 } }
 +</code>
  
 El archivo de cabecera games.h debe ser incluido si queremos utilizar putsprite. En la siguiente imagen vemos la salida de este programa. El archivo de cabecera games.h debe ser incluido si queremos utilizar putsprite. En la siguiente imagen vemos la salida de este programa.
-Figura 3. Los sprites mostrándose en la pantalla de nuestro Spectrum (o de nuestro emulador) 
-Figura 3. Los sprites mostrándose en la pantalla de nuestro Spectrum (o de nuestro emulador) 
  
-Añadiendo movimiento+{{ cursos:z88dk:z88dk7_3.png |Los sprites mostrándose en la pantalla de nuestro Spectrum}} 
 + 
 + 
 + 
 +===== Añadiendo movimiento ===== 
  
 Hacer que un sprite se mueva por pantalla es tan sencillo como borrarlo de donde se encontraba anteriormente y volver a dibujarlo en una nueva posición. Lo interesante será hacer que nuestro coche cambie de aspecto según la orientación en la que se esté moviendo. Hacer que un sprite se mueva por pantalla es tan sencillo como borrarlo de donde se encontraba anteriormente y volver a dibujarlo en una nueva posición. Lo interesante será hacer que nuestro coche cambie de aspecto según la orientación en la que se esté moviendo.
Línea 83: Línea 99:
 Para conseguir esto último, es decir, que se nos muestre un sprite distinto del coche según se esté moviendo hacia arriba, hacia abajo, etc., debemos añadir un par de cambios al archivo coches.h. Lo primero que vamos a hacer es almacenar todos los sprites en un único array de arrays de chars (es decir, en un único array de sprites). Esto lo hacemos colocando después de la definición de los diferentes sprites en coches.h una línea como la siguiente: Para conseguir esto último, es decir, que se nos muestre un sprite distinto del coche según se esté moviendo hacia arriba, hacia abajo, etc., debemos añadir un par de cambios al archivo coches.h. Lo primero que vamos a hacer es almacenar todos los sprites en un único array de arrays de chars (es decir, en un único array de sprites). Esto lo hacemos colocando después de la definición de los diferentes sprites en coches.h una línea como la siguiente:
  
 +<code c>
 char *sprites[9] = { sprite0 , sprite1 , sprite2 , sprite3 , sprite4 , sprite5 , char *sprites[9] = { sprite0 , sprite1 , sprite2 , sprite3 , sprite4 , sprite5 ,
     sprite6 , sprite7 , sprite8 };     sprite6 , sprite7 , sprite8 };
 +</code>
  
 De tal forma que podremos acceder al sprite i utilizando, por ejemplo, sprites[i] en lugar de spritei, lo cual nos va a ser de mucha utilidad. Justo después de esta línea, introducimos las dos líneas siguientes en coches.h: De tal forma que podremos acceder al sprite i utilizando, por ejemplo, sprites[i] en lugar de spritei, lo cual nos va a ser de mucha utilidad. Justo después de esta línea, introducimos las dos líneas siguientes en coches.h:
  
 +<code c>
 int izquierda[] = {4,6,7,5,1,0,2,3}; int izquierda[] = {4,6,7,5,1,0,2,3};
 int derecha[] = {5,4,6,7,0,3,1,2}; int derecha[] = {5,4,6,7,0,3,1,2};
 +</code>
  
 Estos dos arrays los utilizaremos para saber, cada vez que giramos, cuál es el sprite que debemos dibujar en la pantalla. Si los sprites están numerados según la primera figura de este artículo, y ahora mismo se nos muestra en la pantalla el sprite 0, si giramos a la izquierda el que se debería mostrar es el 4. Si volvemos a girar a la izquierda, el que debería dibujarse entonces en la pantalla es el 1, etc. Si por el contrario, estamos mostrando el sprite 0 en la pantalla y giramos a la derecha, se nos debería mostrar el sprite 5. Si volvemos a girar a la derecha, se nos debería mostrar el sprite 3, etc. Estos dos arrays los utilizaremos para saber, cada vez que giramos, cuál es el sprite que debemos dibujar en la pantalla. Si los sprites están numerados según la primera figura de este artículo, y ahora mismo se nos muestra en la pantalla el sprite 0, si giramos a la izquierda el que se debería mostrar es el 4. Si volvemos a girar a la izquierda, el que debería dibujarse entonces en la pantalla es el 1, etc. Si por el contrario, estamos mostrando el sprite 0 en la pantalla y giramos a la derecha, se nos debería mostrar el sprite 5. Si volvemos a girar a la derecha, se nos debería mostrar el sprite 3, etc.
Línea 97: Línea 117:
 Habiendo hecho estas dos modificaciones, ya podemos incluir aquí el código de un programa que llamaremos movimiento.c, que nos pondrá a los mandos de un coche que se mostrará en la pantalla, el cual podremos mover hacia donde queramos, sin ninguna restricción. Habiendo hecho estas dos modificaciones, ya podemos incluir aquí el código de un programa que llamaremos movimiento.c, que nos pondrá a los mandos de un coche que se mostrará en la pantalla, el cual podremos mover hacia donde queramos, sin ninguna restricción.
  
 +<code c>
 #include "stdio.h" #include "stdio.h"
 #include "ctype.h" #include "ctype.h"
Línea 127: Línea 148:
         }         }
 } }
 +</code>
  
 De momento vamos a comenzar con los giros, y ya surcaremos después la pantalla a toda velocidad. El código anterior nos dibuja el coche en una zona cercana al centro de la pantalla, y nos permite girarlo hacia la izquierda y hacia la derecha empleando las teclas o y p respectivamente. Explicamos el código y más adelante indicamos un problema que se muestra durante la ejecuciñón y cómo solucionarlo. De momento vamos a comenzar con los giros, y ya surcaremos después la pantalla a toda velocidad. El código anterior nos dibuja el coche en una zona cercana al centro de la pantalla, y nos permite girarlo hacia la izquierda y hacia la derecha empleando las teclas o y p respectivamente. Explicamos el código y más adelante indicamos un problema que se muestra durante la ejecuciñón y cómo solucionarlo.
Línea 146: Línea 168:
 Queremos que pulsando la tecla 'q' el coche vaya acelerando hasta una velocidad máxima (y por lo tanto, que se vaya moviendo conforme a esa velocidad) y que al pulsar la tecla 'a', el coche vaya frenando hasta detenerse. A continuación vemos como lo hemos resuelto, en el siguiente programa, que sustituirá a nuestro anterior movimiento.c (el código marcado como "Nuevo" es el añadido): Queremos que pulsando la tecla 'q' el coche vaya acelerando hasta una velocidad máxima (y por lo tanto, que se vaya moviendo conforme a esa velocidad) y que al pulsar la tecla 'a', el coche vaya frenando hasta detenerse. A continuación vemos como lo hemos resuelto, en el siguiente programa, que sustituirá a nuestro anterior movimiento.c (el código marcado como "Nuevo" es el añadido):
  
 +<code c>
 #include "stdio.h" #include "stdio.h"
 #include "ctype.h" #include "ctype.h"
Línea 151: Línea 174:
 #include "coches.h" #include "coches.h"
  
 +// Nuevo
 #define CICLOS_BASE 300; #define CICLOS_BASE 300;
  
Línea 159: Línea 183:
         int posicion = 0;         int posicion = 0;
  
-        int velocidad = 0; +        int velocidad = 0;            // nuevo 
-        int ciclos = CICLOS_BASE;+        int ciclos = CICLOS_BASE;     // nuevo
  
         putsprite(spr_xor,x,y,sprites[0]);         putsprite(spr_xor,x,y,sprites[0]);
Línea 168: Línea 192:
                 switch(toupper(getk()))                 switch(toupper(getk()))
                 {                 {
 +                        // nuevo (Principio)
                         case 'Q':                         case 'Q':
                                 if (velocidad < 50)                                 if (velocidad < 50)
Línea 180: Línea 205:
                                }                                }
                                 break;                                 break;
 +                        // nuevo (Fin)
                         case 'O':                         case 'O':
                                 putsprite(spr_xor,x,y,sprites[posicion]);                                 putsprite(spr_xor,x,y,sprites[posicion]);
Línea 192: Línea 218:
                 }                 }
  
 +                // nuevo (Principio)
                 if (velocidad > 0)                 if (velocidad > 0)
                 {                 {
Línea 234: Línea 261:
                 }                 }
         }         }
 +        // nuevo (Fin)
 } }
 +</code>
  
-Lo más evidente es que necesitaremos una variable, en este caso llamada velocidad, que nos permita almacenar la velocidad del coche en un momento dado. Es importante tener en cuenta que en un principio el coche estará parado, por lo que esta velocidad valdrá cero. Hemos añadido también código que hará que nuestra velocidad aumente o disminuya, hasta una velocidad máxima o mínima, al pulsar las teclas 'q' y 'a', respectivamente (este código es el que se ha añadido dentro del switch).+Lo más evidente es que necesitaremos una variable, en este caso llamada //velocidad//, que nos permita almacenar la velocidad del coche en un momento dado. Es importante tener en cuenta que en un principio el coche estará parado, por lo que esta velocidad valdrá cero. Hemos añadido también código que hará que nuestra velocidad aumente o disminuya, hasta una velocidad máxima o mínima, al pulsar las teclas 'q' y 'a', respectivamente (este código es el que se ha añadido dentro del switch).
  
 Lo que a lo mejor queda un poco más esotérico es ver cómo hacemos que el coche, una vez que adquiere velocidad, pueda moverse. La parte del código encargada de esto es la que se encuentra al final. Este código, como es obvio, solo se ejecutará si el coche tiene velocidad. El coche se moverá cuando una variable, llamada ciclos, y a la que se le va restando el valor de la velocidad en cada iteración del bucle principal, llegue a cero. Evidentemente, cuanta mayor sea la velocidad, más rápido llegará ciclos a 0 y cada menos iteraciones del bucle principal se moverá el coche. Lo que a lo mejor queda un poco más esotérico es ver cómo hacemos que el coche, una vez que adquiere velocidad, pueda moverse. La parte del código encargada de esto es la que se encuentra al final. Este código, como es obvio, solo se ejecutará si el coche tiene velocidad. El coche se moverá cuando una variable, llamada ciclos, y a la que se le va restando el valor de la velocidad en cada iteración del bucle principal, llegue a cero. Evidentemente, cuanta mayor sea la velocidad, más rápido llegará ciclos a 0 y cada menos iteraciones del bucle principal se moverá el coche.
Línea 248: Línea 277:
 Las variables del sistema se podrían entender como determinadas direcciones de memoría que modifican el comportamiento del sistema según su valor (más información en la Microhobby especial nº2). En nuestro caso concreto, las variables del sistema que vamos a modificar son REPDEL y REPPER, correspondientes a las direcciones de memoria 23561 y 23562. La primera de ellas indica el tiempo en cincuentavos de segundo que se debe tener pulsada una tecla para que esta se comience a repetir, y la segunda indica el tiempo en cincuentavos de segundo que tarda en producirse esta repetición una vez que se comienza. Si le damos un valor de 1 a estas variables, conseguiremos una respuesta dle teclado rápida, y nos desharemos del efecto tan fastidioso que hemos comentado antes. Las variables del sistema se podrían entender como determinadas direcciones de memoría que modifican el comportamiento del sistema según su valor (más información en la Microhobby especial nº2). En nuestro caso concreto, las variables del sistema que vamos a modificar son REPDEL y REPPER, correspondientes a las direcciones de memoria 23561 y 23562. La primera de ellas indica el tiempo en cincuentavos de segundo que se debe tener pulsada una tecla para que esta se comience a repetir, y la segunda indica el tiempo en cincuentavos de segundo que tarda en producirse esta repetición una vez que se comienza. Si le damos un valor de 1 a estas variables, conseguiremos una respuesta dle teclado rápida, y nos desharemos del efecto tan fastidioso que hemos comentado antes.
  
-En el siguiente código se muestran los cambios que deberíamos realizar al comienzo del programa movimiento.c:+En el siguiente código se muestran los cambios que deberíamos realizar al comienzo del programa **movimiento.c**:
  
 +<code c>
 #include "stdio.h" #include "stdio.h"
 #include "ctype.h" #include "ctype.h"
Línea 263: Línea 293:
         int posicion = 0;         int posicion = 0;
  
-        char *puntero1 = (char *) 23561; +        char *puntero1 = (char *) 23561;       // nuevo 
-        char *puntero2 = (char *) 23562; +        char *puntero2 = (char *) 23562;       // nuevo
  
         int velocidad = 0;         int velocidad = 0;
Línea 274: Línea 303:
         while(1)         while(1)
         {         {
-                *puntero1 = 1; +                *puntero1 = 1;                 // nuevo 
-                *puntero2 = 1;+                *puntero2 = 1;                 // nuevo
                 switch(toupper(getk()))                 switch(toupper(getk()))
                 {                 {
 +</code>
  
 Al mejorar la respuesta del teclado nos ha surgido un nuevo problema... ¡el coche gira demasiado rápido!. Al mejorar la respuesta del teclado nos ha surgido un nuevo problema... ¡el coche gira demasiado rápido!.
Línea 283: Línea 313:
 Será conveniente añadir un contador para que el coche no gire nada más pulsar la tecla correspondiente; mejor que gire cuando la tecle lleve un rato pulsada. A continuación mostramos el código que deberíamos añadir: Será conveniente añadir un contador para que el coche no gire nada más pulsar la tecla correspondiente; mejor que gire cuando la tecle lleve un rato pulsada. A continuación mostramos el código que deberíamos añadir:
  
-        int girando = 0; +<code c> 
-        int contador_izquierda = 0; +        int girando = 0;                       // nuevo 
-        int contador_derecha = 0;+        int contador_izquierda = 0;            // nuevo 
 +        int contador_derecha = 0;              // nuevo
         int velocidad = 0;         int velocidad = 0;
         int ciclos = CICLOS_BASE;         int ciclos = CICLOS_BASE;
Línea 313: Línea 344:
                                 break;                                 break;
                         case 'O':                         case 'O':
 +                                // nuevo bloque, hasta el if (incluído)
                                 contador_derecha = 0;                                 contador_derecha = 0;
                                 contador_izquierda = contador_izquierda + 1;                                 contador_izquierda = contador_izquierda + 1;
                                 girando = 1;                                 girando = 1;
-                                if (contador_izquierda == 3)+                                if (contador_izquierda == 3)  
                                 {                                 {
                                         putsprite(spr_xor,x,y,sprites[posicion]);                                         putsprite(spr_xor,x,y,sprites[posicion]);
                                         posicion = izquierda[posicion];                                         posicion = izquierda[posicion];
                                         putsprite(spr_xor,x,y,sprites[posicion]);                                         putsprite(spr_xor,x,y,sprites[posicion]);
-                                        contador_izquierda = 0;+                                        contador_izquierda = 0;          // nuevo
                                 }                                 }
                                 break;                                 break;
                        case 'P':                        case 'P':
 +                                // nuevo bloque, hasta el if (incluído)
                                 contador_izquierda = 0;                                 contador_izquierda = 0;
                                 contador_derecha = contador_derecha + 1;                                 contador_derecha = contador_derecha + 1;
Línea 333: Línea 366:
                                         posicion = derecha[posicion];                                         posicion = derecha[posicion];
                                         putsprite(spr_xor,x,y,sprites[posicion]);                                         putsprite(spr_xor,x,y,sprites[posicion]);
-                                        contador_derecha = 0; +                                        contador_derecha = 0;           // nuevo  
-                                 +                                
-                                 break;+                                break;
                 }                 }
  
-                if (girando == 0)+                // nuevo (Principio) 
 +                if (girando == 0)     
                 {                 {
                         contador_izquierda = 0;                         contador_izquierda = 0;
                         contador_derecha = 0;                         contador_derecha = 0;
                 }                 }
 +                // nuevo (Fin)
  
                 if (velocidad > 0)                 if (velocidad > 0)
                 {                 {
 +</code>
  
-Nos vamos a basar en tres nuevas variables, girando, contador_izquierda y contador_derecha. Las dos últimas son las que nos van a indicar cuándo hacer el giro. Cada vez que le demos a la tecla de giro a la izquierda, aumentará el valor de contador_izquierda, y cada vez que le demos a la tecla de giro a la derecha, aumentará el valor de contador_derecha. Cuando alguna de ellas valga 3, giraremos a la izquierda o a la derecha, respectivamente.+Nos vamos a basar en tres nuevas variables, //girando, contador_izquierda// //contador_derecha//. Las dos últimas son las que nos van a indicar cuándo hacer el giro. Cada vez que le demos a la tecla de giro a la izquierda, aumentará el valor de contador_izquierda, y cada vez que le demos a la tecla de giro a la derecha, aumentará el valor de contador_derecha. Cuando alguna de ellas valga 3, giraremos a la izquierda o a la derecha, respectivamente.
  
 Una medida que tomamos es que al girar hacia la izquierda, ponemos a cero la variable contador_derecha, que nos dice cuanto tiempo hemos estado girando a la derecha, y viceversa, cuando giramos a la derecha, ponemos a cero la variable contador_izquierda, que nos dice cuánto hemos girado hasta la izquierda hasta el momento. Así evitamos que, en el caso de haber estado girando hacia la izquierda durante un tiempo, por ejemplo, empecemos a girar a la derecha, y que tan solo haga falta rozar la tecla de giro izquierda para volver a girar a la izquierda, lo cual no es demasiado real. Una medida que tomamos es que al girar hacia la izquierda, ponemos a cero la variable contador_derecha, que nos dice cuanto tiempo hemos estado girando a la derecha, y viceversa, cuando giramos a la derecha, ponemos a cero la variable contador_izquierda, que nos dice cuánto hemos girado hasta la izquierda hasta el momento. Así evitamos que, en el caso de haber estado girando hacia la izquierda durante un tiempo, por ejemplo, empecemos a girar a la derecha, y que tan solo haga falta rozar la tecla de giro izquierda para volver a girar a la izquierda, lo cual no es demasiado real.
Línea 360: Línea 395:
 La instrucción en ensamblador que detiene la ejecución hasta que se produce una interrupción es HALT. Y en z88dk, ejecutar una instrucción en ensamblador en cualquier momento es tan sencillo como hacer uso de la función asm, que recibe como parámetro una cadena conteniendo la instrucción que deseamos ejecutar. Por lo tanto, resolver de una vez por todas nuestros problemas de movimiento es tan simple como colocar la instrucción asm("HALT") en los lugares adecuados. A continuación se muestra todo el código de movimiento.c tal como quedaría después de los cambios. La instrucción en ensamblador que detiene la ejecución hasta que se produce una interrupción es HALT. Y en z88dk, ejecutar una instrucción en ensamblador en cualquier momento es tan sencillo como hacer uso de la función asm, que recibe como parámetro una cadena conteniendo la instrucción que deseamos ejecutar. Por lo tanto, resolver de una vez por todas nuestros problemas de movimiento es tan simple como colocar la instrucción asm("HALT") en los lugares adecuados. A continuación se muestra todo el código de movimiento.c tal como quedaría después de los cambios.
  
 +<code c>
 #include "stdio.h" #include "stdio.h"
 #include "ctype.h" #include "ctype.h"
Línea 389: Línea 425:
         *puntero1 = 1;         *puntero1 = 1;
         *puntero2 = 1;         *puntero2 = 1;
-        asm("halt");+        asm("halt");        // nuevo
  
         girando = 0;         girando = 0;
Línea 413: Línea 449:
                 if (contador_izquierda == 3)                 if (contador_izquierda == 3)
                 {                 {
-                    asm("halt");+                    asm("halt");       // nuevo
                     putsprite(spr_xor,x,y,sprites[posicion]);                     putsprite(spr_xor,x,y,sprites[posicion]);
                     posicion = izquierda[posicion];                     posicion = izquierda[posicion];
Línea 422: Línea 458:
             case 'P':             case 'P':
                 contador_izquierda = 0;                 contador_izquierda = 0;
-                                contador_derecha = contador_derecha + 1; +                contador_derecha = contador_derecha + 1; 
-                                girando = 1; +                girando = 1; 
-                                if (contador_derecha == 3) +                if (contador_derecha == 3) 
-                                +                
-                    asm("halt"); +                    asm("halt");       // nuevo 
-                                    putsprite(spr_xor,x,y,sprites[posicion]); +                    putsprite(spr_xor,x,y,sprites[posicion]); 
-                                    posicion = derecha[posicion]; +                    posicion = derecha[posicion]; 
-                                    putsprite(spr_xor,x,y,sprites[posicion]); +                    putsprite(spr_xor,x,y,sprites[posicion]); 
-                                        contador_derecha = 0; +                    contador_derecha = 0; 
-                                 +                 
-                                 break; +                break; 
-        }+            }
  
-                if (girando == 0)+            if (girando == 0)
             {             {
                 contador_izquierda = 0;                 contador_izquierda = 0;
                 contador_derecha = 0;                 contador_derecha = 0;
-        }+            }
  
  
Línea 448: Línea 484:
             {             {
                 ciclos = CICLOS_BASE;                 ciclos = CICLOS_BASE;
-                asm("halt");+                asm("halt");       // nuevo
                 putsprite(spr_xor,x,y,sprites[posicion]);                 putsprite(spr_xor,x,y,sprites[posicion]);
                 switch(posicion)                 switch(posicion)
Línea 486: Línea 522:
     }     }
 } }
 +</code>
  
-Como se puede comprobar, se ha incluido un halt antes de la lectura de teclado, para detener el programa hasta que se pulse una tecla, y después se ha incluido un halt antes de borrar y volver a dibujar el sprite, para esperar el refresco de la pantalla. Como consecuencia, el coche irá más lento, es por ello que también hemos modificado el valor de CICLOS_BASE, la velocidad máxima, y el incremento y decremento de la velocidad. ¿Y os habeis fijado? Gracias a que hemos sincronizado el movimiento del coche con el refresco de la pantalla... ¡este ha dejado de parpadear al moverse!+Como se puede comprobar, se ha incluido un **halt** antes de la lectura de teclado, para detener el programa hasta que se pulse una tecla, y después se ha incluido un halt antes de borrar y volver a dibujar el sprite, para esperar el refresco de la pantalla. Como consecuencia, el coche irá más lento, es por ello que también hemos modificado el valor de CICLOS_BASE, la velocidad máxima, y el incremento y decremento de la velocidad. ¿Y os habeis fijado? Gracias a que hemos sincronizado el movimiento del coche con el refresco de la pantalla... ¡este ha dejado de parpadear al moverse!
  
 Por último, una cosa curiosa: si mientras movemos el coche pasamos por encima de donde pone Bytes: movimiento (correspondiente a la carga desde cinta) veremos como el borrado y la escritura del sprite del coche no afecta en lo mas mínimo a los píxeles que forman parte de ese texto; esto es sin duda otra de las grandes ventajas de usar el modo de dibujado de sprites or exclusiva. Por último, una cosa curiosa: si mientras movemos el coche pasamos por encima de donde pone Bytes: movimiento (correspondiente a la carga desde cinta) veremos como el borrado y la escritura del sprite del coche no afecta en lo mas mínimo a los píxeles que forman parte de ese texto; esto es sin duda otra de las grandes ventajas de usar el modo de dibujado de sprites or exclusiva.
  
-Y ahora, pongamos el coche en una pista+ 
 +===== Y ahora, pongamos el coche en una pista ===== 
  
 Por último vamos a dibujar una pista de neumáticos para limitar un poco el movimiento de nuestro bólido. Vamos además a hacerlo de tal forma que sea muy fácil modificar el trazado, y así la diversión se multiplique durante meses. Si recordamos, el último de los sprites que habíamos definido era el correspondiente a un neumático. Este sprite, com todos los anteriores, tiene un tamaño de 10x10 (quizá un poco grande, para hacer circuitos más complicados quizas no hubiera estado mal utilizar sprites de neumáticos más pequeños). Por último vamos a dibujar una pista de neumáticos para limitar un poco el movimiento de nuestro bólido. Vamos además a hacerlo de tal forma que sea muy fácil modificar el trazado, y así la diversión se multiplique durante meses. Si recordamos, el último de los sprites que habíamos definido era el correspondiente a un neumático. Este sprite, com todos los anteriores, tiene un tamaño de 10x10 (quizá un poco grande, para hacer circuitos más complicados quizas no hubiera estado mal utilizar sprites de neumáticos más pequeños).
Línea 497: Línea 536:
 Dentro de coches.h es donde definiremos el trazado de la pista. Si consideramos la pantalla como un array de 18x24 casillas de tamaño 10x10 (el mismo que los neumáticos), podemos indicar el recorrido de la pista creando un array, donde colocaremos ceros en aquellas casillas donde no vaya a haber neumático, y unos en las casillas en las que sí. Al principio de coches.h definimos unas constantes para el tamaño del circuito: Dentro de coches.h es donde definiremos el trazado de la pista. Si consideramos la pantalla como un array de 18x24 casillas de tamaño 10x10 (el mismo que los neumáticos), podemos indicar el recorrido de la pista creando un array, donde colocaremos ceros en aquellas casillas donde no vaya a haber neumático, y unos en las casillas en las que sí. Al principio de coches.h definimos unas constantes para el tamaño del circuito:
  
 +<code c>
 #define ALTURA_CIRCUITO 18 #define ALTURA_CIRCUITO 18
 #define ANCHURA_CIRCUITO 24 #define ANCHURA_CIRCUITO 24
 +</code>
  
 y al final del mismo archivo incluimos el siguiente array: y al final del mismo archivo incluimos el siguiente array:
  
 +<code c>
 short circuito1[] =  { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, short circuito1[] =  { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
     0, 0, 0, 0, 0, 0, 0 };     0, 0, 0, 0, 0, 0, 0 };
Línea 543: Línea 585:
     circuito12, circuito13, circuito14, circuito15, circuito16, circuito17,     circuito12, circuito13, circuito14, circuito15, circuito16, circuito17,
     circuito18};     circuito18};
 +</code>
  
-Con un poco de imaginación podemos vislumbrar en ese array un circuito cerrado en forma de O. Si ahora creamos un fichero llamado juego.c, que contenga el mismo código que movimiento.c, pero añadiéndole el que aparece en color rojo a continuación, podremos por fin ver nuestro circuito en la pantalla, tal como se ve en la siguiente imagen:+Con un poco de imaginación podemos vislumbrar en ese array un circuito cerrado en forma de O. Si ahora creamos un fichero llamado **juego.c**, que contenga el mismo código que movimiento.c, pero añadiéndole el que aparece en color rojo a continuación, podremos por fin ver nuestro circuito en la pantalla, tal como se ve en la siguiente imagen:
  
 +<codec>
 void main(void) void main(void)
 { {
Línea 551: Línea 595:
         int y = 140;         int y = 140;
         int posicion = 0;         int posicion = 0;
-        short int i+        short int ij;         // nuevo
-        short int j;+
  
         char *puntero1 = (char *) 23561;         char *puntero1 = (char *) 23561;
Línea 563: Línea 606:
         int ciclos = CICLOS_BASE;         int ciclos = CICLOS_BASE;
  
 +        // nuevo (Principio)
         for (i=0;i<30;i++)         for (i=0;i<30;i++)
                 printf("\n");                 printf("\n");
  
-        for (i=0;i +        for (i=0;i<ALTURA_CIRCUITO;i++) 
- +                for (j=0;j<ANCHURA_CIRCUITO;j++) 
-        putsprite(spr_xor,x,y,sprites[0]);+                        if (circuito[i][j] == 1) 
 +                                putsprite(spr_xor,j*10+1,i*10+1,sprites[8]); 
 +        // nuevo (Fin)
  
         while(1)         while(1)
Línea 575: Línea 621:
                 *puntero2 = 1;                 *puntero2 = 1;
                 asm("halt");                 asm("halt");
 +</code>
 +
  
-Figura 4. Nuestro coche dispuesto a ser el rey de la pista +{{ cursos:z88dk:z88dk7_4.png |Nuestro coche dispuesto a ser el rey de la pista}}
-Figura 4. Nuestro coche dispuesto a ser el rey de la pista+
  
 Hay que tener en cuenta que hemos cambiado el valor inicial de la coordenada y del coche para que éste quede dentro de la pista. También hemos creado dos variables, i y j que nos van a servir de contadores en un par de bucles. Hay que tener en cuenta que hemos cambiado el valor inicial de la coordenada y del coche para que éste quede dentro de la pista. También hemos creado dos variables, i y j que nos van a servir de contadores en un par de bucles.
Línea 914: Línea 961:
  
  
-    * [[http://www.speccy.org/magazinezx/revistas/12/src/z88dk_coches.zip|Código fuente de los ejemplos]]+    * [[https://magazinezx.speccy.org/12/src/z88dk_coches.zip|Código fuente de los ejemplos]]