Diferencias
Muestra las diferencias entre dos versiones de la página.
Próxima revisión | Revisión previaÚltima revisiónAmbos lados, revisión siguiente | ||
cursos:z88dk:sprites3 [09-08-2007 13:47] – creado sromero | cursos:z88dk:sprites3 [24-02-2020 19:20] – falvarez | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
- | http://www.speccy.org/ | + | Publicado originalmente en [[https://magazinezx.speccy.org/ |
+ | |||
+ | ====== Sprites en Z88DK (III): La librería SpritePack ====== | ||
+ | |||
+ | |||
+ | |||
+ | ===== Introducción a Sprite Pack ===== | ||
+ | |||
+ | |||
+ | En artículos anteriores de la revista | ||
+ | |||
+ | Para hacernos la vida más fácil a la hora de programar usando sprites disponemos de la librería **Sprite Pack**; la página principal del proyecto es http:// | ||
+ | |||
+ | Sprite Pack es una librería multipropósito, | ||
+ | |||
+ | Durante los siguientes artículos nos centraremos especialmente en el módulo de sprites, con la idea de ser utilizado en el seno de nuestras aplicaciones z88dk para Spectrum, aunque durante el desarrollo de nuestros ejemplos tendremos que ir usando también algunas pequeñas partes del resto de los módulos. Vamos a dar un pequeño paso atrás con respecto a entregas anteriores, pues vamos a aprender de nuevo a dibujar sprites desde el principio, aunque utilizando Sprite Pack en esta ocasión. Podría parecer que esto es un poco inútil, pero ya veremos más adelante que las ventajas de utilizar esta capa por encima de z88dk no tardarán en llegar. | ||
+ | |||
+ | |||
+ | |||
+ | ===== Instalación ===== | ||
+ | |||
+ | Lo primero que debemos hacer es descargar la librería de su página (en el momento de escribir este artículo, la última versión era la 2.2): | ||
+ | |||
+ | http:// | ||
+ | |||
+ | Tras obtener el fichero comprimido, lo descomprimimos en cualquier directorio de trabajo. El resultado será la carpeta z88dk, en cuyo interior encontraremos a su vez otro directorio llamado work, en el que en último lugar descubriremos un nuevo directorio llamado splib2, en el que nos deberemos situar para realizar la compilación. | ||
+ | |||
+ | Al tratarse de una librería para el manejo, entre otras cosas, de sprites de la pantalla, debemos comprobar que el código de la librería se va a compilar para las características peculiares del Spectrum antes de continuar. Lo único que tendremos que hacer será editar el fichero SPconfig.def, | ||
+ | |||
+ | Sprite Pack incluye un fichero Makefile.bat que creará la librería en una máquina Windows. Solamente sería necesario en este tipo de entorno que se abriera una ventana de MS-DOS, se acudiera al directorio donde está el código fuente, y tras inicializar las variables de entorno que permiten compilar con z88dk, teclear Makefile. En Linux es un poco más complicado, y deberemos modificar ese archivo para que funcione. Para ahorrar trabajo al lector, se incluye un fichero Makefile.sh, | ||
+ | |||
+ | Sea cual sea el sistema operativo que utilicemos, como resultado obtendremos un nuevo fichero **splib2.lib**, | ||
+ | |||
+ | |||
+ | ===== Nuestro primer ejemplo ===== | ||
+ | |||
+ | |||
+ | Sprite Pack tiene una forma particular de actualizar la pantalla. Realizar una actualización completa y simultánea de toda ella simultáneamente haría necesario el uso de rutinas en ensamblador demasiado específicas, | ||
+ | |||
+ | Otros conceptos que deberemos tener claros son el de **backtile** y **sprite**. Comprender la diferencia entre ambos es necesario para desarrollar nuestras aplicaciones. La pantalla está dividida en un array de tamaño 32x24 de celdas de caracteres (a su vez de tamaño 8x8). Cada una de estas celdas podrá contener sólo un backtile y uno o más sprites. Los backtiles se pueden entender como el " | ||
+ | |||
+ | De momento nos centraremos en los backtiles. La función básica para dibujarlos es **sp_PrintAtInv**, | ||
+ | |||
+ | Veamos un ejemplo. El siguiente código se encarga de dibujar letras ' | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #pragma output STACKPTR=61440 | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | # | ||
+ | di | ||
+ | # | ||
+ | | ||
+ | | ||
+ | # | ||
+ | ei | ||
+ | # | ||
+ | |||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | if (rand()%10 > 5) | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Veamos línea por línea de qué va todo esto. Las dos primeras se corresponden con las sentencias #include necesarias; en este caso la librería estándar y el archivo de cabecera de la librería Sprite Pack. | ||
+ | |||
+ | La sentencia **#pragma** deberemos incluirla siempre al principio de nuestros programas escritos para la librería Sprite Pack, antes del método main. Lo que se dice con ella es que se desea comenzar la ejecución del programa con el puntero de la pila del Z80 apuntando a la dirección 61440. Al hacerlo de esta forma, evitaremos que la función sp_initialize destruya cierta información de la memoria. | ||
+ | |||
+ | Las primeras líneas de código en el interior de main(), entre el primer #asm y el último #endasm tendremos que ponerlas también siempre en nuestros programas que usen Sprite Pack. No es necesario entrar en detalle, pero diremos que este código tiene algo que ver con deshabilitar interrupciones para que ciertas funciones como sp_Invalidate funcionen. | ||
+ | |||
+ | Y por fin comienzan las líneas interesantes. La función **sp_Initialize** es necesaria para que el módulo de sprites de la librería comience a funcionar. En este caso, los parámetros hacen que la pantalla se inicialice escribiendo espacios en blanco, con color de fondo (paper) blanco y tinta negra (ink). | ||
+ | |||
+ | A continuación nos introducimos en el bucle principal, que se va a ejecutar de forma ininterrumpida por toda la eternidad (a menos que detengamos la ejecución del programa). La estructura es muy sencilla. Primero se llama a **sp_UpdateNow** para que se redibujen las celdas de la pantalla donde hubo algún cambio. A continuación se obiene un número al azar entre 1 y 10 (mediante la función rand()), y en función de su valor llamaremos a s**p_PrintAtInv** con unos parámetros u otros. | ||
+ | |||
+ | El método sp_PrintAtInv dibuja un backtile en la pantalla e invalida la posición donde dicho backtile ha sido colocado, para que la celda correspondiente sea redibujada tras llamar a sp_UpdateNow. Los dos primeros parámetros indican la posición del backtile (que también los obtenemos al azar), a continuación el color de papel y de tinta (con la misma sintaxis que en el caso de la función sp_Initialize) y por último indicamos el carácter a escribir. Este carácter se corresponde con un UDG, como en BASIC. De momento no hemos asociado ningún UDG a la letra x, por lo que se mostrará en pantalla será la letra x apareciendo en posiciones al azar, teniendo o bien color de tinta rojo y papel cyan o al revés. | ||
+ | |||
+ | La última línea del bucle, **sp_Pause**, | ||
+ | |||
+ | {{ cursos: | ||
+ | |||
+ | |||
+ | ===== Nuestro primer ejemplo con un Sprite ===== | ||
+ | |||
+ | |||
+ | En esta sección vamos a dibujar un sprite simple sobre un fondo un poco más complejo. Para comprender cómo podemos crear un fondo más complejo, hemos de recordar que los backtiles pueden entenderse como si fueran UDGs de BASIC; sin embargo, hasta ahora solo hemos utilizado caracteres alfanuméricos. ¿De qué forma definimos gráficos para estos backtiles? | ||
+ | |||
+ | Mediante la función **sp_TileArray** asociamos un determinado UDG 8x8 a un carácter, de tal forma que al escribir dicho carácter como backtile en la pantalla, aparecerá su UDG correspondiente. Esta función recibe como parámetro el carácter al que queremos asociar el UDG y un array de tipo uchar con la definición del UDG. Ese array contendrá un valor hexadecimal por cada fila del UDG, utilizando la misma notación que vimos en capítulos anteriores para la creación de sprites de tamaño 8x8 con z88dk. Por ejemplo, observemos el siguiente código: | ||
+ | |||
+ | <code c> | ||
+ | uchar fondo[] = {0x55, | ||
+ | |||
+ | sp_TileArray(' | ||
+ | sp_Initialize(INK_WHITE | PAPER_BLACK, | ||
+ | </ | ||
+ | |||
+ | Con la primera línea creamos un array llamado fondo que contendrá la definición de un UDG de tipo " | ||
+ | |||
+ | ¿Y cómo definimos un sprite? De forma mucho más fácil a la vista en artículos anteriores, en los que se usaba z88dk sin ningún añadido. Nada más simple que una notación como la siguiente: | ||
+ | |||
+ | <code c> | ||
+ | #asm | ||
+ | |||
+ | ._sprite1 | ||
+ | defb @00111100, @11000011 | ||
+ | defb @01000010, @10000001 | ||
+ | defb @10000001, @00000000 | ||
+ | defb @10100101, @00000000 | ||
+ | defb @10000001, @00000000 | ||
+ | defb @10011001, @00000000 | ||
+ | defb @01011010, @10000001 | ||
+ | defb @00111100, @11011011 | ||
+ | |||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Como se puede observar, volvemos a hacer uso de código ensamblador empotrado en el interior de nuestro código C, entre las directivas #asm y #endasm. En este caso concreto estamos definiendo un único sprite 8x8, para el cual necesitaremos dos bloques de bits de ese tamaño. El de la izquierda es el dibujo en sí mismo del sprite, que en nuestro caso en una pequeña carita con la boca abierta. El bloque de la derecha indica las transparencias; | ||
+ | |||
+ | A este bloque de datos que marcan un sprite de 8x8 le hemos llamado sprite1. Este nombre nos servirá para referenciar este código ensamblador desde el código C. Para ver cómo dibujar el sprite en la pantalla, nada mejor que un pequeño ejemplo: | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #pragma output STACKPTR=61440 | ||
+ | |||
+ | extern struct sp_Rect *sp_ClipStruct; | ||
+ | #asm | ||
+ | LIB SPCClipStruct | ||
+ | ._sp_ClipStruct | ||
+ | #endasm | ||
+ | |||
+ | |||
+ | extern uchar sprite1[]; | ||
+ | uchar fondo[] = {0x55, | ||
+ | |||
+ | |||
+ | void *my_malloc(uint bytes) | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | |||
+ | void *u_malloc = my_malloc; | ||
+ | void *u_free = sp_FreeBlock; | ||
+ | |||
+ | |||
+ | |||
+ | main() | ||
+ | { | ||
+ | | ||
+ | |||
+ | # | ||
+ | di | ||
+ | # | ||
+ | | ||
+ | | ||
+ | # | ||
+ | ei | ||
+ | # | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | bicho = sp_CreateSpr(sp_MASK_SPRITE, | ||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | } | ||
+ | |||
+ | #asm | ||
+ | |||
+ | ._sprite1 | ||
+ | defb @00111100, @11000011 | ||
+ | defb @01000010, @10000001 | ||
+ | defb @10000001, @00000000 | ||
+ | defb @10100101, @00000000 | ||
+ | defb @10000001, @00000000 | ||
+ | defb @10011001, @00000000 | ||
+ | defb @01011010, @10000001 | ||
+ | defb @00111100, @11011011 | ||
+ | |||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Comentemos línea por línea el programa. La sentencia #pragma ya la conocemos del ejemplo anterior, así que no hace falta que nos detengamos en ella. A continuación se define una estructura de la forma siguiente: | ||
+ | |||
+ | <code c> | ||
+ | extern struct sp_Rect *sp_ClipStruct; | ||
+ | #asm | ||
+ | LIB SPCClipStruct | ||
+ | ._sp_ClipStruct | ||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Esta estructura tan confusa está sacada de spritepack.h y define un rectángulo que cubre toda la superficie de la pantalla. Existen muchas definiciones como ésta dentro de dicho fichero de cabecera. Para qué sirven lo veremos mucho más adelante, cuando tratemos el tema de las colisiones. De momento deberemos saber que lo necesitamos para poder dibujar el sprite en la pantalla. | ||
+ | |||
+ | La línea //extern uchar sprite1[]//; | ||
+ | |||
+ | El siguiente código: | ||
+ | |||
+ | <code c> | ||
+ | void *my_malloc(uint bytes) | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | |||
+ | void *u_malloc = my_malloc; | ||
+ | void *u_free = sp_FreeBlock; | ||
+ | </ | ||
+ | |||
+ | entra dentro de lo que estaremos obligados a insertar en nuestro programa cada vez que queramos añadirle sprites, aunque no entendamos muy bien de qué se trata. Se obtiene a partir del módulo de manejo de memoria de Sprite Pack y sirve para que el programa pueda obtener memoria bajo demanda. Esto es necesario debido a la forma en que la pantalla es actualizada al usar esta librería. Además de las estructuras creadas por el programador, | ||
+ | |||
+ | Y por fin comienza el método **main**. En primer lugar definimos la variable que va a contener el sprite que vamos a mostrar por pantalla. Esta variable es de tipo **struct sp_SS**, una estructura que contiene diversa infomación sobre un sprite, como su localización y su tamaño. A continuación, | ||
+ | |||
+ | Y continuamos con las dos líneas siguientes, cuyo funcionamiento ha sido explicado anteriormente: | ||
+ | |||
+ | <code c> | ||
+ | p_TileArray(' | ||
+ | sp_Initialize(INK_WHITE | PAPER_BLACK, | ||
+ | </ | ||
+ | |||
+ | La línea posterior es nueva, y permite definir el color del borde de la pantalla (como la instrucción BORDER de BASIC). A continuación otra línea un tanto complicada, correspondiente a la llamada a la función **sp_AddMemory**, | ||
+ | |||
+ | Las líneas que más nos interesan de este ejemplo son las siguientes: | ||
+ | |||
+ | <code c> | ||
+ | bicho = sp_CreateSpr(sp_MASK_SPRITE, | ||
+ | sp_MoveSprAbs(bicho, | ||
+ | </ | ||
+ | |||
+ | Con la primera de ellas creamos el sprite en si mismo. El resultado de la llamada se almacenará en la variable de tipo struct sp_SS bicho declarada anteriormente, | ||
+ | |||
+ | Situaremos el sprite creado sobre la pantalla mediante el uso de la función **sp_MoveSprAbs**, | ||
+ | |||
+ | Como primer parámetro, el sprite a mover. Como segundo parámetro, el rectángulo que hace referencia a toda la pantalla definido anteriormente. El tercer parámetro hace referencia a la animación, y tampoco hablaremos de él en esta ocasión. El cuarto y el quinto, al bloque de la pantalla donde situaremos el sprite. Decíamos anteriormente que la pantalla estaba dividida en bloques de 8x8; pues bien, con estos dos parámetros indicamos la celda donde colocaremos nuestro sprite. Para conseguir una colocación más exacta, podemos usar los dos últimos parámetros, | ||
+ | |||
+ | Por último, con **sp_UpdateNow()** redibujamos las porciones de la pantalla que lo necesiten, y con while(1) dejamos nuestro programa en constante ejecución, hasta el resto de los días, o hasta que lo detengamos. El resultado se puede observar en la siguiente captura de pantalla. | ||
+ | |||
+ | {{ cursos: | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Un sprite grande y nervioso ===== | ||
+ | |||
+ | |||
+ | En este apartado veremos un pequeño ejemplo que introduce algunos conceptos interesantes, | ||
+ | |||
+ | Pero comencemos por el principio. ¿Cómo definimos sprites que contengan más de un bloque 8x8? Con Sprite Pack, los sprites grandes se definen por columnas. Podemos definir una columna de un sprite grande, compuesta por uno o más bloques de 8x8, bajo una misma etiqueta de código ensamblador. Un sprite de más de una columna se formará a partir de varias de estas definiciones en ensamblador. | ||
+ | |||
+ | Por ejemplo, mostramos como quedaría un sprite con un tamaño de dos bloques de alto por dos bloques de ancho listo para ser usado con Sprite Pack: | ||
+ | |||
+ | <code c> | ||
+ | #asm | ||
+ | |||
+ | ._bicho1 | ||
+ | defb @00000011, @11111100 | ||
+ | defb @00000100, @11111000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00000100, @11111000 | ||
+ | |||
+ | defb @00000011, @11111100 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho2 | ||
+ | defb @11100000, @00011111 | ||
+ | defb @00010000, @00001111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @10001000, @00000111 | ||
+ | defb @10010000, @00001111 | ||
+ | |||
+ | defb @11100000, @00011111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Como se puede observar, definimos dos columnas para el sprite, cada una de ellas formada a su vez por dos sprites de tamaño 8x8, incluyendo su máscara de transparencias. Para utilizar el sprite en el código deberíamos hacer algo similar a esto: | ||
+ | |||
+ | <code c> | ||
+ | extern uchar bicho1[]; | ||
+ | extern uchar bicho2[]; | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | struct sp_SS *spriteBicho; | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Se debe hacer uso, en primer lugar, de **sp_CreateSpr** para asignar la primera columna del sprite a la variable de tipo **struct sp_SS**, y en segundo lugar, de **sp_AddColSpr**, | ||
+ | |||
+ | Es importante destacar que todas las columnas de un mismo sprite deben de ser definidas de forma contigua en la memoria. Esto se traduce en que tenemos que definirlas de forma contigua también en nuestro código. | ||
+ | |||
+ | Sin embargo, hay algo que hasta ahora no hemos tenido en cuenta, y es debido a que no hemos movido nuestros sprites por la pantalla. Cuando trasladamos sprites usando el pixel y no el bloque como unidad de medida (los dos últimos parámetros de sp_MoveSprAbs y sp_MoveSprRel servían para esto) veremos como los sprites no son correctamente dibujados; solo se redibuja la parte del sprite más a la izquierda que cabe dentro de una misma celdilla de la pantalla. Un truco para evitar esto es crear sprites un poco más anchos y más altos de lo que realmente necesitamos. Para ello, añadimos una nueva columna en blanco, y en cada columna, un nuevo bloque en blanco al final. En el caso concreto de nuestro sprite 2x2 anterior, deberíamos definirlo de esta forma: | ||
+ | |||
+ | <code c> | ||
+ | #asm | ||
+ | |||
+ | ._bicho1 | ||
+ | defb @00000011, @11111100 | ||
+ | defb @00000100, @11111000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00000100, @11111000 | ||
+ | |||
+ | defb @00000011, @11111100 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho2 | ||
+ | defb @11100000, @00011111 | ||
+ | defb @00010000, @00001111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @10001000, @00000111 | ||
+ | defb @10010000, @00001111 | ||
+ | |||
+ | defb @11100000, @00011111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho3 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Por lo tanto, nuestro sprite 2x2 se convierte en un sprite 3x3 al añadir una nueva columna a la derecha y un nuevo bloque en la parte inferior de cada columna. Nuestro código a la hora de usar el sprite debería ser en este caso algo más parecido a esto: | ||
+ | |||
+ | <code c> | ||
+ | extern uchar bicho1[]; | ||
+ | extern uchar bicho2[]; | ||
+ | extern uchar bicho3[]; | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Como queda patente, indicamos que el tamaño en bloques de cada columna es 3 al llamar a la función sp_CreateSPr, | ||
+ | |||
+ | A continuación se muestra un ejemplo completo, con nuestro sprite moviéndose al azar por la pantalla, por medio de la función // | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #pragma output STACKPTR=61440 | ||
+ | |||
+ | extern struct sp_Rect *sp_ClipStruct; | ||
+ | #asm | ||
+ | LIB SPCClipStruct | ||
+ | ._sp_ClipStruct | ||
+ | #endasm | ||
+ | |||
+ | extern uchar bicho1[]; | ||
+ | extern uchar bicho2[]; | ||
+ | extern uchar bicho3[]; | ||
+ | uchar hash[] = {0x55, | ||
+ | |||
+ | void *my_malloc(uint bytes) | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | void *u_malloc = my_malloc; | ||
+ | void *u_free = sp_FreeBlock; | ||
+ | |||
+ | |||
+ | main() | ||
+ | { | ||
+ | char dx, dy, i; | ||
+ | | ||
+ | |||
+ | #asm | ||
+ | di | ||
+ | #endasm | ||
+ | sp_InitIM2(0xf1f1); | ||
+ | sp_CreateGenericISR(0xf1f1); | ||
+ | #asm | ||
+ | ei | ||
+ | #endasm | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | dx = dy = 1; | ||
+ | |||
+ | if (rand()%2 == 0) // izquierda | ||
+ | dx = -dx; | ||
+ | else if (rand()%2 == 0) // derecha | ||
+ | dx = 0; | ||
+ | if (rand()%2 == 0) // arriba | ||
+ | dy = -dy; | ||
+ | else if (rand()%2 == 0) // abajo | ||
+ | dy = 0; | ||
+ | |||
+ | sp_MoveSprRel(spriteBicho, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | #asm | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho1 | ||
+ | defb @00000011, @11111100 | ||
+ | defb @00000100, @11111000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001011, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00001000, @11110000 | ||
+ | defb @00000100, @11111000 | ||
+ | |||
+ | defb @00000011, @11111100 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @01111100, @10000011 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho2 | ||
+ | defb @11100000, @00011111 | ||
+ | defb @00010000, @00001111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @01101000, @00000111 | ||
+ | defb @00001000, @00000111 | ||
+ | defb @10001000, @00000111 | ||
+ | defb @10010000, @00001111 | ||
+ | |||
+ | defb @11100000, @00011111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00011000, @11100111 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00001100, @11110011 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00111110, @11000001 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | ._bicho3 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | defb @00000000, @11111111 | ||
+ | |||
+ | #endasm | ||
+ | </ | ||
+ | |||
+ | Ya deberíamos entender la práctica totalidad de este código, que mueve un sprite al azar por la pantalla, por lo que tan solo haremos dos apuntes: | ||
+ | |||
+ | * Los movimientos indicados con sp_MoveSprRel son movimientos relativos, por lo que si queremos desplazarnos tan solo un pixel, ya sea a la izquierda o a la derecha, ya sea arriba o abajo (penúltimo y último parámetros respectivamente) usaremos valores +1 y -1. | ||
+ | * Se ha añadido un bloque vacío antes de definir nuestro sprite, al final del código anterior; esto es así porque debemos asegurarnos de que haya un bloque en blanco encima de cada columna (manías del ensamblador generado por Sprite Pack). | ||
+ | |||
+ | {{ cursos: | ||
+ | |||
+ | ===== ¿Y ahora qué? ===== | ||
+ | |||
+ | |||
+ | Hemos aprendido las más básicas funcionalidades de Sprite Pack para la creación de sprites. Ya somos capaces de crear nuestros propios sprites (de forma más sencilla a la que se ha realizado en artículos anteriores) y de moverlos, aunque sea de manera aleatoria, por la pantalla (sin necesidad de hacer llamadas de ensamblador para el manejo de interrupciones, | ||
+ | |||
+ | Vemos que no es necesario borrar un sprite usando la máscara XOR antes de volver a dibujarlo para simular movimiento, tal como hacíamos con z88dk sin Sprite Pack, y también observamos como somos capaces de añadir transparencias en los sprites de forma muy simple. Volvemos a ganar en facilidad de uso. | ||
+ | |||
+ | En el siguiente artículo añadiremos colores a nuestros sprites y aprenderemos a moverlos con el teclado. También veremos como borrar de forma efectiva un sprite. Si el espacio lo permite (y si no es así, que no cunda el pánico, pues se verá en números posteriores de la revista) codificaremos un simple juego funcional con todo lo aprendido. | ||
+ | |||
+ | |||
+ | ===== Enlaces ===== | ||
+ | |||
+ | |||
+ | * [[https:// | ||
+ |