Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa Próxima revisiónAmbos lados, revisión siguiente | ||
cursos:z88dk:sprites4 [10-08-2007 08:40] – sromero | cursos:z88dk:sprites4 [23-02-2020 15:08] – [Enlaces] falvarez | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
- | > ÍNDICE DE REVISTAS < | + | ====== Sprites en Z88DK (IV) ====== |
- | Número 14 - Diciembre 2006 ZX Certified webmaster speccy.org | + | |
- | + | ||
- | LA LIBRERIA SPRITE PACK (II) | ||
- | INTRODUCCIÓN | + | ===== La librería Sprite Pack (II) ===== |
- | En la entrega anterior de la revista comenzamos a examinar la librería Sprite Pack y aprendimos a dibujar elementos gráficos sobre la pantalla. También aprendimos que la pantalla estaba dividida en 32x24 " | + | |
+ | En la entrega anterior de la revista comenzamos a examinar la librería Sprite Pack y aprendimos a dibujar elementos gráficos sobre la pantalla. También aprendimos que la pantalla estaba dividida en 32x24 " | ||
En esta ocasión vamos a añadir un par de sencillos detalles más antes de comenzar a escribir nuestro propio juego; un código que luego podrá formar parte de un producto más elaborado. En concreto veremos cómo hacer reaccionar nuestro programa ante la pulsación de teclas por parte del usuario, y cómo mover un sprite utilizando este dispositivo de entrada. También aprenderemos como añadir color a los sprites. Para nuestras explicaciones haremos uso como base del código sprite2.c que se creó en la anterior entrega, y en el que se definía un sprite de tamaño 2x1 que se desplazaba al azar. | En esta ocasión vamos a añadir un par de sencillos detalles más antes de comenzar a escribir nuestro propio juego; un código que luego podrá formar parte de un producto más elaborado. En concreto veremos cómo hacer reaccionar nuestro programa ante la pulsación de teclas por parte del usuario, y cómo mover un sprite utilizando este dispositivo de entrada. También aprenderemos como añadir color a los sprites. Para nuestras explicaciones haremos uso como base del código sprite2.c que se creó en la anterior entrega, y en el que se definía un sprite de tamaño 2x1 que se desplazaba al azar. | ||
- | MOVIENDO LOS SPRITES CON EL TECLADO | + | |
+ | ===== Moviendo los sprites con el teclado ===== | ||
Si queremos mover un sprite por la pantalla utilizando el teclado, lo primero que deberemos hacer en nuestro programa es declarar una estructura del siguiente tipo: | Si queremos mover un sprite por la pantalla utilizando el teclado, lo primero que deberemos hacer en nuestro programa es declarar una estructura del siguiente tipo: | ||
+ | <code c> | ||
struct sp_UDK keys; | struct sp_UDK keys; | ||
+ | </ | ||
- | La variable keys nos permitirá asociar teclas de nuestro teclado con los diferentes controles de un joystick virtual haciendo uso de la función sp_Lookup_Key, | + | La variable keys nos permitirá asociar teclas de nuestro teclado con los diferentes controles de un joystick virtual haciendo uso de la función |
- | keys.up = sp_LookupKey(' | + | <code c> |
- | | + | keys.up = sp_LookupKey(' |
- | | + | keys.down = sp_LookupKey(' |
- | | + | keys.right = sp_LookupKey(' |
- | | + | keys.left = sp_LookupKey(' |
+ | keys.fire = sp_LookupKey(' | ||
+ | </ | ||
Según este ejemplo, cada vez que pulsáramos ' | Según este ejemplo, cada vez que pulsáramos ' | ||
- | La función sp_JoyKeyboard devuelve una máscara de bits indicándonos qué controles del joystick están accionados en ese momento debido a que sus correspondientes teclas están pulsadas. La forma de interpretar el valor devuelto por esta función es igual que con cualquier máscara de bits en C, y se puede contemplar en el siguiente ejemplo, que consiste en el archivo sprite2.c de la anterior entrega modificado de tal forma que movamos el sprite precisamente con la combinación de teclas indicada anteriormente. Para ello realizamos movimientos relativos del sprite, cambiando cómo desplazamos el sprite en x y en y (dx y dy respectivamente) según los controles de nuestro joystick virtual que estén siendo accionados. El código nuevo respecto a la versión anterior del archivo se muestra | + | La función |
+ | <code c> | ||
#include < | #include < | ||
#include < | #include < | ||
Línea 43: | Línea 47: | ||
extern uchar bicho2[]; | extern uchar bicho2[]; | ||
extern uchar bicho3[]; | extern uchar bicho3[]; | ||
- | struct sp_UDK keys; | + | struct sp_UDK keys; // nuevo |
uchar hash[] = {0x55, | uchar hash[] = {0x55, | ||
Línea 75: | Línea 79: | ||
| | ||
+ | // nuevo (principio) | ||
| | ||
| | ||
Línea 80: | Línea 85: | ||
| | ||
| | ||
+ | // nuevo (fin) | ||
| | ||
Línea 88: | Línea 94: | ||
| | ||
| | ||
- | + | ||
+ | // nuevo (principio) | ||
| | ||
if ((i & sp_FIRE) == 0) { | if ((i & sp_FIRE) == 0) { | ||
Línea 103: | Línea 110: | ||
else if ((i & sp_DOWN) != 0) | else if ((i & sp_DOWN) != 0) | ||
dy = 0; | dy = 0; | ||
- | + | // nuevo (fin) | |
| | ||
} | } | ||
Línea 203: | Línea 211: | ||
#endasm | #endasm | ||
+ | </ | ||
- | Al probar el ejemplo nos habremos encontrado con un problema bastante grave: al no controlar cuándo el sprite sobrepasa los límites de la pantalla, es posible desplazar nuestro " | + | Al probar el ejemplo nos habremos encontrado con un problema bastante grave: al no controlar cuándo el sprite sobrepasa los límites de la pantalla, es posible desplazar nuestro " |
- | ¡Ya podemos mover a nuestro bicho con el teclado! | + | |
- | ¡Ya podemos mover a nuestro bicho con el teclado! | + | |
- | El código anterior se ha modificado para añadir dos nuevas funcionalidades. Al pulsar la tecla r, nuestro | + | {{ cursos: |
+ | El código anterior se ha modificado para añadir dos nuevas funcionalidades. Al pulsar la tecla r, nuestro " | ||
+ | |||
+ | <code c> | ||
#include < | #include < | ||
#include < | #include < | ||
Línea 239: | Línea 249: | ||
char dx,dy,i | char dx,dy,i | ||
| | ||
- | uint reset, | + | uint reset, |
- | int borde = 1; | + | int borde = 1; // nuevo |
Línea 262: | Línea 272: | ||
| | ||
| | ||
- | reset = sp_LookupKey(' | + | reset = sp_LookupKey(' |
- | | + | |
| | ||
Línea 288: | Línea 298: | ||
else if ((i & sp_DOWN) != 0) | else if ((i & sp_DOWN) != 0) | ||
dy = 0; | dy = 0; | ||
+ | // nuevo (principio) | ||
if (sp_KeyPressed(reset)) | if (sp_KeyPressed(reset)) | ||
| | ||
Línea 303: | Línea 314: | ||
| | ||
} | } | ||
+ | // nuevo (fin) | ||
} | } | ||
} | } | ||
Línea 401: | Línea 413: | ||
#endasm | #endasm | ||
+ | </ | ||
Como veremos más adelante, deberemos implementar algún mecanismo para limitar la zona por donde nuestros sprites se van a desplazar. La forma de hacer esto es mediante simples comparaciones, | Como veremos más adelante, deberemos implementar algún mecanismo para limitar la zona por donde nuestros sprites se van a desplazar. La forma de hacer esto es mediante simples comparaciones, | ||
- | AÑADIENDO COLOR | + | |
+ | |||
+ | |||
+ | ===== Añadiendo color ===== | ||
Un paso importante para poder tener como resultado un videojuego vistoso y que entre por los ojos es aprovechar los colores de nuestro Spectrum y disponer de una combinación agradable de cromaticidad en los sprites de nuestro juego. Los colores también permitirán distinguir con mayor facilidad nuestro personaje de los enemigos y el resto de elementos de la pantalla. Ya vimos en entregas anteriores que en el caso de los tiles era bastante sencillo modificar la tonalidad de la tinta y el papel: | Un paso importante para poder tener como resultado un videojuego vistoso y que entre por los ojos es aprovechar los colores de nuestro Spectrum y disponer de una combinación agradable de cromaticidad en los sprites de nuestro juego. Los colores también permitirán distinguir con mayor facilidad nuestro personaje de los enemigos y el resto de elementos de la pantalla. Ya vimos en entregas anteriores que en el caso de los tiles era bastante sencillo modificar la tonalidad de la tinta y el papel: | ||
+ | <code c> | ||
sp_TileArray(' | sp_TileArray(' | ||
sp_Initialize(INK_WHITE | PAPER_BLACK, | sp_Initialize(INK_WHITE | PAPER_BLACK, | ||
+ | </ | ||
- | pero en el caso de los sprites la cosa se complica un poco más y vamos a tener que dar unas cuantas " | + | pero en el caso de los sprites la cosa se complica un poco más y vamos a tener que dar unas cuantas " |
- | El siguiente código muestra, en color rojo, las modificaciones realizadas al programa anterior para poder añadirle color al sprite de nuestro bicho, y que pasaremos a explicar a continuación: | + | El siguiente código muestra las modificaciones realizadas al programa anterior para poder añadirle color al sprite de nuestro bicho, y que pasaremos a explicar a continuación: |
+ | <code c> | ||
#include < | #include < | ||
#include < | #include < | ||
Línea 425: | Línea 445: | ||
#endasm | #endasm | ||
+ | // nuevo (principio) | ||
extern uchar *sp_NullSprPtr; | extern uchar *sp_NullSprPtr; | ||
#asm | #asm | ||
Línea 430: | Línea 451: | ||
._sp_NullSprPtr | ._sp_NullSprPtr | ||
# | # | ||
+ | // nuevo (fin) | ||
extern uchar bicho1[]; | extern uchar bicho1[]; | ||
Línea 447: | Línea 469: | ||
+ | // nuevo (principio) | ||
uchar n; | uchar n; | ||
void addColour(struct sp_CS *cs) | void addColour(struct sp_CS *cs) | ||
Línea 468: | Línea 491: | ||
| | ||
} | } | ||
+ | // nuevo (fin) | ||
Línea 503: | Línea 527: | ||
| | ||
| | ||
- | | + | |
| | ||
Línea 637: | Línea 661: | ||
#endasm | #endasm | ||
+ | </ | ||
Como se puede comprobar, tras crear el sprite del bicho, hacemos uso de la siguiente instrucción: | Como se puede comprobar, tras crear el sprite del bicho, hacemos uso de la siguiente instrucción: | ||
+ | <code c> | ||
sp_IterateSprChar(spriteBicho, | sp_IterateSprChar(spriteBicho, | ||
+ | </ | ||
- | Esto significa que vamos a usar una función llamada addColour, y que deberemos haber definido anteriormente dentro del archivo con el código, para modificar las propiedades de spriteBicho. Aunque la instrucción sólo aparece una vez, es como si estuviéramos llamando a la función addColour una vez por cada bloque de 8x8 que forma el sprite del bicho (recordemos que el sprite de nuestro bicho está formado por 3 columnas de 3 bloques de 8x8, por lo que la función addColour será llamada un total de 9 veces). La forma que tiene está función es la siguiente: | + | Esto significa que vamos a usar una función llamada |
+ | <code c> | ||
uchar n; | uchar n; | ||
void addColour(struct sp_CS *cs) | void addColour(struct sp_CS *cs) | ||
Línea 665: | Línea 693: | ||
| | ||
} | } | ||
+ | </ | ||
- | Justo antes se define una variable global llamada n, y que no será más que un contador que nos permitirá saber en cuál de las nueve llamadas a la función addColour nos encontramos. Ya dentro de dicho método se observa como su valor se incrementa de uno en uno en cada llamada. | + | Justo antes se define una variable global llamada n, y que no será más que un contador que nos permitirá saber en cuál de las nueve llamadas a la función |
- | Como hemos repetido varias veces, la función addColour será llamada una vez por cada bloque 8x8 que forme nuestro sprite, recibiendo como parámetro un struct de tipo sp_CS que nos va a permitir modificar diversas características de dicho bloque del sprite. Uno de los campos de ese struct es colour, que como su propio nombre indica, es el indicado para añadir color. Gracias al valor de n, podremos conocer en cuál de todos los bloques del sprite nos encontramos (empezando por el 0, los bloques están ordenados de arriba a abajo y de izquierda a derecha, por lo que en el caso de nuestro bicho, aun estando compuesto por 3x3 bloques, sólo nos interesará colorear aquellos para los que n vale 0,1,3 y 4, que son los que no están vacíos) y asignarle un color de tinta y papel modificando el valor del campo colour del struct sp_CS, tal como se puede observar en el código anterior. | + | Como hemos repetido varias veces, |
- | Sólo deberemos colorear los bloques 0,1,3 y 4 de nuestro bicho, pues el bloque 2 se corresponde con el último de la primera columna (que está vacío), el bloque 5 con el último de la segunda columna (que también está vacío) y los bloques 6,7 y 8 con la última columna de todas, también vacía, y que se añadió para que no hubiera problemas al desplazar el sprite. En el caso de los bloques 2 y 5 lo más correcto hubiera sido utilizar el valor TRANSPARENT para el campo colour (aunque en nuestro ejemplo hemos sido un poco transgresores y el bloque 2 no lo hemos hecho transparente). Para la última columna (bloques para los que n vale más que 5), sin embargo, asignamos el valor sp_NullSprPtr al campo colour. Este valor, que ha sido definido anteriormente en el programa de la siguiente forma: | + | Sólo deberemos colorear los bloques 0, 1, 3 y 4 de nuestro bicho, pues el bloque 2 se corresponde con el último de la primera columna (que está vacío), el bloque 5 con el último de la segunda columna (que también está vacío) y los bloques 6,7 y 8 con la última columna de todas, también vacía, y que se añadió para que no hubiera problemas al desplazar el sprite. En el caso de los bloques 2 y 5 lo más correcto hubiera sido utilizar el valor //TRANSPARENT// para el campo colour (aunque en nuestro ejemplo hemos sido un poco transgresores y el bloque 2 no lo hemos hecho transparente). Para la última columna (bloques para los que n vale más que 5), sin embargo, asignamos el valor //sp_NullSprPtr// al campo colour. Este valor, que ha sido definido anteriormente en el programa de la siguiente forma: |
+ | <code c> | ||
extern uchar *sp_NullSprPtr; | extern uchar *sp_NullSprPtr; | ||
#asm | #asm | ||
Línea 677: | Línea 707: | ||
._sp_NullSprPtr | ._sp_NullSprPtr | ||
#endasm | #endasm | ||
+ | </ | ||
evitará que esa columna vacía " | evitará que esa columna vacía " | ||
Y ya está, ya le hemos dado color a nuestro bicho (eso sí, una combinación bastante psicodélica). Cada vez que queramos hacer lo mismo con cualquier otro sprite, tan solo deberemos seguir la receta anterior, pues es algo mecánico. | Y ya está, ya le hemos dado color a nuestro bicho (eso sí, una combinación bastante psicodélica). Cada vez que queramos hacer lo mismo con cualquier otro sprite, tan solo deberemos seguir la receta anterior, pues es algo mecánico. | ||
- | Nuestro sprite a color. Evidentemente, | + | |
- | Nuestro sprite a color. Evidentemente, | + | {{ cursos: |
===== Un nuevo juego ===== | ===== Un nuevo juego ===== | ||
Línea 1085: | Línea 1117: | ||
</ | </ | ||
- | Se han definido un total de cuatro sprites. El sprite de tipo roca (dividido en tres columnas denominadas roca1, roca2 y roca3) será el que representará los obstáculos que encontrará nuestro intrépido esquiador en su descenso. Para el protagonista de nuestro juego se han definido tres sprites divididos cada uno de ellos en dos columnas: skiCentrado que será el que se mostrará cuando el esquiador esté descendiendo sin que lo movamos a izquierda o derecha, skiIzquierda que representa nuestro esquiador desplazándose hacia la izquierda, y skiDerecha que a su vez representa al mismo esquiador desplazándose a la derecha. | + | Se han definido un total de cuatro sprites. El sprite de tipo //roca// (dividido en tres columnas denominadas |
Como debemos reservar memoria para los sprites que vayamos a mostrar por pantalla, lo haremos para cada una de las tres posiciones del esquiador (de tal forma que mostraremos la adecuada según el movimiento del jugador) y un número fijo de rocas. Es probable que no todas las rocas sean visibles simultáneamente durante el juego, pero hacerlo de esta forma simplifica en gran medida el diseño, sin tener que hacer grandes esfuerzos para reservar o liberar memoria cuando sea necesario. En concreto, la constante NUM_ROCAS es la que vamos a usar para indicar el número de rocas que se van a crear. Se define un array llamado spriteRoca con tantas posiciones como las indicadas por la constante anteriormente mencionada, y en cada posición de dicho array se reserva memoria para un sprite de tipo roca que es coloreado con su correspondiente función addColour: | Como debemos reservar memoria para los sprites que vayamos a mostrar por pantalla, lo haremos para cada una de las tres posiciones del esquiador (de tal forma que mostraremos la adecuada según el movimiento del jugador) y un número fijo de rocas. Es probable que no todas las rocas sean visibles simultáneamente durante el juego, pero hacerlo de esta forma simplifica en gran medida el diseño, sin tener que hacer grandes esfuerzos para reservar o liberar memoria cuando sea necesario. En concreto, la constante NUM_ROCAS es la que vamos a usar para indicar el número de rocas que se van a crear. Se define un array llamado spriteRoca con tantas posiciones como las indicadas por la constante anteriormente mencionada, y en cada posición de dicho array se reserva memoria para un sprite de tipo roca que es coloreado con su correspondiente función addColour: | ||
Línea 1208: | Línea 1240: | ||
- | * [[http://www.speccy.org/ | + | * [[https://magazinezx.speccy.org/ |