22
TUTORIAL ALLEGRO Escrito en francés por: Emeric Poupon Traducción libre por: Ernesto Castelán Chávez (Sir_Lance) Introducción Para comenzar, debo precisar que es necesario un conocimiento correcto de C/C++ y que Allegro ya debe estar correctamente instalado. - Durante todo el tutorial, los prototipos de las funciones propias de Allegro serán mostradas en negritas. - /*Los comentarios se mostrarán en un azul verdoso muy característico*/ - Los tipos de variables en marrón (int, float, …) incluyendo los tipos de variables específicos de Allegro (BITMAP, DATAFILE, …) - Las estructuras de control en azul (if, else, while, {}, …) - Los números en rojo (0, 1, 324, 4, …) - Los comandos del preprocesador en verde (#define, #include) - “Las cadenas de caracteres en gris” 1. ¿Para que sirve Allegro? Vayamos directo al grano: ¿Qué es Allegro? Es una librería que dispone de todo lo necesario para programar un juego de video. En otras palabras, Allegro nos brinda una solución para gestionar gráficos, sonido, teclado, ratón, temporizadores… en fin, ¡todo lo que se necesite! Originalmente Allegro fue creada por Shawn Hargreaves, para Atari ST, luego pasó rápidamente a DOS. Las primeras versiones de la librería datan de principios de 1996. ¡Es un proyecto que no nació ayer! Rápidamente, los programadores de Allegro orientaron su programación hacia una librería multiplataforma. Ahora se puede usar Allegro para DOS (DJGPP, Watcom), Windows (MSVC, Mingw32, Cygwin, Borland), Linux (consola), Unix (X), BeOS, QNX, MacOS (MPW). Ya lo ha de haber comprendido, la gran fuerza de Allegro reside en el hecho de que es soportada por un gran número de Sistemas Operativos (SO). Concretamente, puede compilar sus programas bajo cualquier compilador (de los mencionados arriba) sin cambiar una sola línea de código. De alguna manera, Allegro seleccionará, solito, los controladores correctos según el SO. Por ejemplo un programa bajo Windows utiliza la aceleración DirectDraw, mientras que bajo Linux puede disfrutar de los controladores X11. De la misma manera para DirectSound y DirectInput para Windows. En contraparte, el 3D no es el fuerte de esta librería: ninguna aceleración por hardware será dada: nada de Direct3D. Sólo OpenGL es muy bien soportado, pero con la ayuda de un pequeño parche de Allegro (AllegroGL). Mencionamos también que es una librería gratuita y libre, y por tanto el código fuente esta disponible. 2. Allegro: Ejemplo de aplicación Un juego que utilice Allegro puede ser una aplicación de Windows, en ese caso, DirectX 7.0 (mínimo) será necesario para el funcionamiento del juego. Allegro puede estar en un DLL (alleg42.dll) o puede, también, estar estático, en cuyo caso el código de Allegro utilizado se encontrará en el ejecutable (es el caso para DOS). Les aseguro que en los otros SO también podrán escoger una compilación estática dinámica, así que no se preocupen. Para ver un ejemplo, tienen a su disposición el demo oficial (en la carpeta allegro/demo) que reagrupa en un solo programa algunas características muy interesantes de la librería. No olvide “linkear” le librería correctamente, o si no, obtendrá un mar de errores. (Consulte los archivos de ayuda en cada compilador).

Tutorial Allegro

Embed Size (px)

Citation preview

Page 1: Tutorial Allegro

TUTORIAL ALLEGRO Escrito en francés por: Emeric Poupon Traducción libre por: Ernesto Castelán Chávez (Sir_Lance)

Introducción Para comenzar, debo precisar que es necesario un conocimiento correcto de C/C++ y que Allegro ya debe estar correctamente instalado. - Durante todo el tutorial, los prototipos de las funciones propias de Allegro serán mostradas en negritas . - /*Los comentarios se mostrarán en un azul verdoso m uy característico*/ - Los tipos de variables en marrón (int , float , …) incluyendo los tipos de variables específicos de Allegro (BITMAP, DATAFILE, …) - Las estructuras de control en azul (if , else , while , {} , …) - Los números en rojo (0, 1, 324 , 4, …) - Los comandos del preprocesador en verde ( #define , #include ) - “Las cadenas de caracteres en gris”

1. ¿Para que sirve Allegro? Vayamos directo al grano: ¿Qué es Allegro? Es una librería que dispone de todo lo necesario para programar un juego de video. En otras palabras, Allegro nos brinda una solución para gestionar gráficos, sonido, teclado, ratón, temporizadores… en fin, ¡todo lo que se necesite! Originalmente Allegro fue creada por Shawn Hargreaves, para Atari ST, luego pasó rápidamente a DOS. Las primeras versiones de la librería datan de principios de 1996. ¡Es un proyecto que no nació ayer! Rápidamente, los programadores de Allegro orientaron su programación hacia una librería multiplataforma. Ahora se puede usar Allegro para DOS (DJGPP, Watcom), Windows (MSVC, Mingw32, Cygwin, Borland), Linux (consola), Unix (X), BeOS, QNX, MacOS (MPW). Ya lo ha de haber comprendido, la gran fuerza de Allegro reside en el hecho de que es soportada por un gran número de Sistemas Operativos (SO). Concretamente, puede compilar sus programas bajo cualquier compilador (de los mencionados arriba) sin cambiar una sola línea de código. De alguna manera, Allegro seleccionará, solito, los controladores correctos según el SO. Por ejemplo un programa bajo Windows utiliza la aceleración DirectDraw, mientras que bajo Linux puede disfrutar de los controladores X11. De la misma manera para DirectSound y DirectInput para Windows. En contraparte, el 3D no es el fuerte de esta librería: ninguna aceleración por hardware será dada: nada de Direct3D. Sólo OpenGL es muy bien soportado, pero con la ayuda de un pequeño parche de Allegro (AllegroGL). Mencionamos también que es una librería gratuita y libre, y por tanto el código fuente esta disponible.

2. Allegro: Ejemplo de aplicación

Un juego que utilice Allegro puede ser una aplicación de Windows, en ese caso, DirectX 7.0 (mínimo) será necesario para el funcionamiento del juego. Allegro puede estar en un DLL (alleg42.dll) o puede, también, estar estático, en cuyo caso el código de Allegro utilizado se encontrará en el ejecutable (es el caso para DOS). Les aseguro que en los otros SO también podrán escoger una compilación estática dinámica, así que no se preocupen. Para ver un ejemplo, tienen a su disposición el demo oficial (en la carpeta allegro/demo) que reagrupa en un solo programa algunas características muy interesantes de la librería. No olvide “linkear” le librería correctamente, o si no, obtendrá un mar de errores. (Consulte los archivos de ayuda en cada compilador).

Page 2: Tutorial Allegro

Tampoco olvide que tiene a su disposición toda una gama de ejemplos muy prácticos. Es, de hecho, gracias a ellos que aprendí a servirme “correctamente” de Allegro. Todas las descripciones de las funciones están disponibles en el archivo de ayuda generado en la instalación de Allegro (/docs/html/allegro.html). Están muy bien explicadas y es muy útil para descubrir todas las funciones de Allegro. En fin, si realmente quiere descubrir todo el potencial de esta librería, puede ir al depósito oficial de los juegos Allegro: www.allegro.cc

3. Las bases de Allegro: un primer programa Comencemos un pequeño programa básico que nos servirá de ejemplo. Para comenzar hay que incluir el archivo de cabecera de la librería, cuyo nombre es “allegro.h ”. ¡Cuidado! Por el momento no hay necesidad de incluir WinMain ni “windows.h ”, olvídese de todo lo que concierne a un solo SO. /* Incluimos el archivo de cabecera de Allegro */ #include <allegro.h> /* Y comenzamos con la función main */ int main() { Muy importante, antes de hacer lo que sea con la librería, hay que llamar la función de inicialización. /* Función general de inicialización */ allegro_init(); ¡Perfecto! ¡Allegro está inicializado! Ahora, si instaláramos el teclado y el ratón sería mucho más práctico… /* Inicializa el teclado */ install_keyboard(); Si la función tuvo éxito, devuelve 0, sino, devuelve un número negativo. Podemos considerar que no vale la pena verificar el resultado puesto que las probabilidades de error son mínimas. /* Inicializa el ratón */ install_mouse(); Aquí, la cosa se pone más interesante: si la función fracasa, devuelve -1, sino, devuelve el número de botones del ratón que Allegro puede manejar. Es importante efectuar una verificación puesto que los usuarios de DOS no necesariamente tienen un controlador de ratón que funcione… Entonces podemos escribir: /* Si la función fracasa, entonces… */ if (install_mouse() == -1 ) { /* Mostramos el mensaje de error */ allegro_message( “Error! %s” , allegro_error); /* Y salimos del programa */ return -1 ; } /*Ahora, sabemos que el ratón funciona correctament e */ En este punto me dirán: ¿Qué es allegro_message ? ¿Y allegro_error ? Aquí el prototipo de allegro_message : void allegro_message( char *msg, …); Esta función utiliza el mismo formato de salida que la función printf . Es, por lo tanto, muy cómoda de utilizar. ¡Cuidado, esta función sólo debe ser utilizada en modos no gráficos! Sólo la puede utilizar si no ha inicializado el modo gráfico, o si pasó explícitamente en modo texto. En este caso, por ejemplo, no hemos iniciado aún el

Page 3: Tutorial Allegro

modo gráfico, podemos entonces usará la función sin problemas. En los SO que trabajan en modo texto o consola, como DOS y Unix, los mensajes aparecerán, normalmente, en la consola. Para Windows y BeOS, esto mostrará un cuadro de diálogo con un botón “OK” abajo. Tendrá como título el nombre del ejecutable del programa. Esta función es muy practica para señalar errores, independientemente del SO. extern char allegro_error[ALLEGRO_ERROR_SIZE]; Es una cadena de caracteres usada por algunas funciones de Allegro, como set_gfx_mode o install_mouse . Sirve para reportar los errores que pudieron surgir en la inicialización. Si el usuario quiere saber la naturaleza del error, la cadena allegro_error contiene la descripción del problema: no queda más que mostrarla (con allegro_message por ejemplo). ALLEGRO_ERROR_SIZE, para no enredarlo, sencillamente es el tamaño de la cadena de caracteres. Bueno, ya tenemos el ratón y el teclado. ¿Acaso no es genial? Solamente que sería mejor pasar al modo gráfico... para comenzar, hay que definir el número de colores que se usarán en el modo de vídeo. En es decir por cuántos bits estará codificado cada píxel (8, 15, 16, 24 o 32 bits). Cuanto más alto sea el número de bits, más amplia será la paleta de colores disponible. Por ejemplo para 16 bits, tiene derecho a 2^16 = 65,536 colores, para 24 bits 2^24 igual 16,777,216 colores. El caso de los colores de 8 bits es más particular, lo dejaremos de lado por el momento. /* Establecemos el número de colores para el modo g ráfico. Por el momento será 16 bits lo cual será lo suficientemente amplio para nu estra pantalla. Tiene la ventaja de ser muy expandido y por lo tanto fácilmente sopo rtada por otras tarjetas de vídeo */ set_color_depth( 16); Ahora pasaremos al modo gráfico propiamente dicho. Para ello utilizaremos la siguiente función: set_gfx_mode(GFX_AUTODETECT, 640 , 480 , 0, 0); Pero ahora, ¿cómo diablos utilice esta función? He aquí su prototipo: int set_gfx_mode( int card, int width, int height, int v_width, int v_height); Comenzaremos por lo fácil, la función devuelve cero en caso de éxito, sino, devuelve un número negativo. “int card ” no es muy descriptivo, es el índice del modo gráfico que queremos utilizar. Aquí están los diferentes valores que debemos escribir (las definiciones están en alguna parte de allegro.h):

- GFX_AUTODETECT. No nos ocupamos de nada, dejamos que Allegro escoja el mejor controlador. Se pondrá en modo ventana si la resolución no está disponible en pantalla completa y si el SO lo soporta. - GFX_AUTODETECT_FULLSCREEN. Forzamos a Allegro a escoger un controlador de pantalla completa - GFX_AUTODETECT_WINDOWED. Forzamos a Allegro a escoger un controlador en modo ventana - GFX_TEXT. Es muy útil para regresar al modo texto, en ese caso, podemos poner cero para las dimensiones de la pantalla.

Naturalmente, existen otros valores, pero son más específicos para cada SO (por lo tanto tendremos que evitar usarlos), regresaremos a ello más tarde, por el momento sólo vemos las bases. Los valores width y height representan el tamaño gráfico de la pantalla creada. Puede acceder a las dimensiones de la pantalla gracias a los macros SCREEN_W y SCREEN_H inicializados por la función. Por el momento, no nos ocuparemos de v_width y de v_height (por lo tanto podemos poner el valor cero). Como todo programa respetable, hay que verificar si no hay errores. Agregaremos, entonces, las pruebas necesarias:

Page 4: Tutorial Allegro

if (set_gfx_mode(GFX_AUTODETECT, 640 , 480 , 0, 0) != 0) { /*Como han seguido bien mis explicaciones, han de s aber que allegro_message se utiliza únicamente en modo texto, es por eso que ut ilizamos GFX_TEXT, para estar seguros de pasar a modo texto */ set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); /*aquí el comando para modo texto*/ allegro_message( "Imposible iniciar el modo video!\n%s\n" , allegro_error); return 1; //y no nos olvidamos de salir... } //aquí todo salió bien... SCREEN_W = 640 SCREEN_H = 480; ¡Listo! ¡Terminamos de inicializar nuestro pequeño programa! Podríamos añadir sonido e instalar un control de juego, pero eso será en capítulos próximos. Por ahora, haremos que el programa se interrumpa cuando presionemos la tecla ESC. Agregaremos entonces algunas líneas de código. while ( ! key[KEY_ESC]) { //Mientras que ESC no se oprima... /*Borramos la pantalla. Analizaremos esta función e n capítulos posteriores*/ clear_bitmap(screen); } Aquí vemos un aspecto importante de la librería, la gestión de teclas oprimidas por el usuario. extern volatile char key[KEY_MAX]; Es muy simple: todas las teclas de Allegro, están agrupadas en este arreglo (de tamaño KEY_MAX, claro). Es un tablero en que cada tecla tiene un índice. Para conocer la lista de todas las teclas, abra el archivo “allegro/keyboard.h ”, todo está definido ahí. Normalmente, este arreglo representa el estado de la tecla actual. Es decir si está o no presionada: por lo tanto sólo es usada para leer. Se “modifica” solito gracias a las interrupciones del teclado. Puede, claro está, simular que oprime una tecla, pero eso es otra historia… Aquí otros ejemplos de uso (¡No los copie en el código! Sería inútil, pues printf no funciona en modo gráfico): if (key[KEY_ENTER]) printf (“La tecla Enter está oprimida \n” ); if (key[KEY_SPACE]) printf( “La barra espaciadora está oprimida \n” ); ¡No nos olvidemos de salir del programa! return 0; //Y así salimos del programa } END_OF_MAIN(); Quizás se haya dado cuenta que las aplicaciones de Windows tienen su punto de entrada en WinMain y no en main . Es por eso que la macro END_OF_MAIN() es necesaria: permita utilizar la función main sin importar el SO. Escríbala justo al final de la función main y no tendrá ninguna advertencia o error a la hora de compilar. ¡Nada de que preocuparse! Pues esto es todo como bases de inicialización a Allegro. Si usted copió y comprendió el programa de ejemplo, a medida que fue explicado, no debería tener problemas. Claro que es muy básico, no hace más que mostrar una pantalla en negro (si no ocurrió ningún error de inicialización antes) y esperar que oprima la tecla ESC para salir. En el próximo capítulo nos interesaremos por una parte esencial de los juegos de video: ¡Gráficos en la pantalla!

Page 5: Tutorial Allegro

4. Gráficos en pantalla

4.1. Presentación general Pasaremos ahora a la parte de los gráficos en pantalla, sin duda la parte más importante. Para esto, mostraremos un disco blanco desplazándose hacia la derecha de la pantalla. Estará exactamente a la mitad de la altura de nuestra pantalla. El usuario deberá presionar ENTER para lanzar el disco. Modifiquemos, pues, nuestro ciclo de espera (el que verifica que la tecla ESC no esté oprimida): /* Declaramos nuestra variable, que representa la p osición del círculo. Si programa en C, escríbala al inicio de la función main */ double circle_pos_x = 0; /* Esperamos pacientemente a que el usuario oprima ENTER para mostrar y mover el disco */ while (¡key[KEY_ENTER]); while (¡key[KEY_ESC]) { //Mientras la tecla ESC no esté oprimida… /* Comenzamos borrando la pantalla */ clear_bitmap(screen); /* Dibujamos un circulo “lleno” de blanco (color 255,255,255) */ circlefill(screen,( int )circle_pos_x,SCREEN_H/ 2, 50, makecol( 255 , 255 , 255 ) ); //Movemos el disco a la derecha 0.1 pixel circle_pos_x += 0.1; /* Verificamos si el disco sale de la pantalla, si es así salimos del ciclo y por tanto finalizamos el pr ograma */ if (circle_pos_x – 50 >= SCREEN_W) break ; } ¡No se preocupe! Aclararemos todo esto… circlefill es la función que nos permitirá mostrar un disco en pantalla. void circlefill( BITMAP *bmp, int x, int y, int radius, int color); x y y representan las coordenadas del centro del disco en la pantalla. Todas las coordenadas están dadas con respecto a la esquina superior izquierda de la pantalla. radius representa, sencillamente, el radio en píxeles. Como se ha de haber dado cuenta, aparece un nuevo tipo de dato aquí, y se trata de BITMAP. No se preocupe, hablaremos de esto después. extern BITMAP *screen; Sólo recuerde que screen es una variable global que apunta hacia un BITMAP (zona de imagen) que representa la memoria de vídeo, y por lo tanto apunta directamente a la pantalla. El tamaño de este mapa de bits es SCREEN_W x SCREEN_H. Entonces bmp es el mapa de bits en el cual dibujaremos nuestro disco, es el destino de la función de dibujo. color es, como lo indica su nombre, el color del disco. Lo mejor es usar una pequeña función para definirlo. int makecol( int r, int g, int b); Esta función permite definir un color, independientemente del modo de colores que estemos usando. Podemos llamar a esta función en los modos de 8, 15, 16, 24 y 32 bits por píxel. r representa la parte roja, g el la parte verde y b la parte azul, es decir que devuelve un color en formato RGB. Cada componente del color puede

Page 6: Tutorial Allegro

tomar valores de 0 a 255. makecol( 255 , 255 , 255 ) devuelve, por lo tanto, el código del color blanco del modo de video actual. Ahora, si ejecuta el programa, se dará cuenta que hay unos horribles parpadeos en la pantalla. De hecho, no vemos un disco, sino una sucesión de líneas negras y blancas. ¡Realmente no es el efecto que buscábamos! ¿Entonces, dónde está el problema? Es muy sencillo, para programar correctamente un juego, no debemos jamás escribir directamente sobre la pantalla, a menos que no nos importe para nada el resultado, pero ese no es nuestro caso. Utilizaremos, pues, un nuevo modo de impresión en pantalla, el “double buffering” . El principio es muy básico: en vez de dibujar en la pantalla, dibujaremos en un mapa de bits que tendremos guardado en la memoria principal (RAM). Luego, al final de nuestro ciclo, copiaremos de golpe el contenido de ese mapa de bits (hablamos del buffer ) en la pantalla. No es el método que da los resultados más espectaculares, pero ¡será mucho mejor! Definamos de una vez por todas el tipo BITMAP (podrá encontrar la declaración en los archivos de cabecera de la librería): typedef struct BITMAP { // El tamaño del mapa de bits en píxeles (w ancho, h alto) int w, h; } ; Naturalmente, existen otras variables en el tipo BITMAP, pero no las utilizaremos. Así, para conocer el tamaño de la pantalla hay otro método, screen -> w que representa el ancho y screen -> h que representa el alto. No olvide nunca que Allegro trabaja con punteros hacia BITMAP (o sea BITMAP*) . Declararemos pues nuestro buffer de video: BITMAP* buffer; //La variable que apunta al buffer de video Ahora, es necesario crear un mapa de bits de las mismas dimensiones que la pantalla. Este paso debe hacerse después de iniciar el modo de gráfico. buffer = create_bitmap(SCREN_W,SCREEN_H); BITMAP* create_bitmap( int width, int height); Esta función crea un mapa de bits de anchura width y de altura height . Devuelve un puntero hacia el BITMAP creado. Normalmente esta imagen creada en la memoria no esta completamente vacía (negra) y deben quedar residuos. Hay que vaciarla antes de poder utilizarla. Para vaciar un mapa de bits al color 0 (negro) utilizamos esta función: void clear_bitmap( BITMAP* bitmap); Esta función puede ser acelerada por hardware, dando como resultado un rendimiento increíble. clear_bitmap(buffer); //Así de sencillo Escribiremos una vez más nuestro ciclo de espera: while (!key[KEY_ESC]) { //Mientras ESC no se oprima… //Empezamos por vaciar el buffer clear_bitmap(buffer); //Dibujamos el círculo blanco en el buffer //El blanco es el color 255,255,255 cireclefill(buffer, ( int )circle_pos_x, SCREEN_W/ 2, 50, makecol( 255 , 255 , 255) ); circle_pos_x += 0.1 ; //Movemos el círculo a la derecha 0.1 píxeles

Page 7: Tutorial Allegro

/* Verificamos si el disco sale de la pantalla, si es así salimos del ciclo y por ende del programa también */ if (circle_pos_x – 50 >= SCREEN_W) break ; //Ahora copiamos el contenido del buffer el pan talla blit(buffer, screen, 0,0,0,0,SCREEN_W,SCREEN_H) ; } Reemplazamos, pues, screen por buffer en todas las funciones necesarias. No queda más que descubrir la función blit… Esta función permite copiar una zona de un mapa de bits hacia una zona de otro mapa de bits. void blit( BITMAP* source, BI TMAP* dest, int source_x, int source_y, int dest_x, int dest_y, int whidth, int eight); ¡He aquí una función interesante! source queda claro que es el mapa de bits de origen (en este caso buffer ), dest es el mapa de bits de destino (en este caso screen ). source_x y source_y representan las coordenadas del origen del mapa de bits del que vamos a copiar. dest_x y dest_y representan las coordenadas a partir de las cuales comenzaremos a dibujar en el mapa de bits de destino. Finalmente width y eight representan las dimensiones que queremos copiar. En nuestro caso, queríamos copiar íntegramente el buffer en pantalla. Es por eso que comenzamos a dibujar desde las coordenadas (0,0) y que dibujamos todo el buffer (SCREEN_W y SCREEN_H). Ejecute el programa de nuevo y se llevará una sorpresa. ¡No más parpadeos rebeldes! Las cosas pintan bien… Por otra parte la velocidad del disco puede verse reducida notablemente. Es totalmente normal, pues la computadora ocupa la mayor parte de su tiempo a mostrar en pantalla. Mostrar un pequeño disco en pantalla es mucho más rápido que copiar todo el buffer en pantalla, por lo tanto es preferible cambiar la línea de desplazamiento por: ++circle_pos_x; //Desplazaremos el disco 1 píxel esta vez Aquí, la velocidad del disco depende directamente de la velocidad de nuestro ordenador (la impresión en pantalla es un factor muy limitativo de de la velocidad del programa). Inclusive en un ordenador poderosísimo, no podrá alcanzar velocidades astronómicas (en termino de imágenes por segundo) pues hay que esperar que cada imagen sea dibujada por la tarjeta de video. No se desanime, en los próximos capítulos hablaremos del tiempo real , es decir, que su juego vaya a la misma velocidad sin importar la potencia de la computadora en la que se este ejecutando.

4.2 Impresión de texto por pantalla Una de las funciones “genialisísimas” de Allegro es imprimir texto en la pantalla de una manera muy sencilla. En efecto, en modo gráfico, es absolutamente imposible usar la función printf y otras de ese tipo. Allegro nos da una amplia gama de funciones que nos permiten efectuar esas tareas de forma automática. Modifiquemos otra vez el ciclo de espera principal de nuestro pequeño programa para que muestre la resolución actual de la pantalla y la posición de nuestro pequeño disco blanco: while (!key[KEY_ESC]) { //Mientras no se oprima ESC… //Comenzamos por borrar el (buffer); clear_bitmap(buffer); /* Imprimimos una cadena de caracteres en las c oordenadas (0,0) y para el color utilizaremos un azul violeta vivo (150,150,255) */ textout(buffer, font, “Estoy escribiendo texto!” , 0, 0, makecol( 150 , 150 , 255 ) ); //Movemos el disco 0.1 píxeles a la derecha cricle_pos_x += 0.1 ;

Page 8: Tutorial Allegro

/*Verificamos que el disco no salga de la panta lla, si es asi, salimos del boucle y por tanto terminamos el programa */ if (circle_pos_x – 50 >= SCREEN_W) break ; /* Ahora hay que copier el contenido del buffer en la pantalla*/ blit(buffer, screen, 0, 0, 0, 0,SCREEN_W, SCREEN_H); } Hemos utilizado la función textout para imprimir el texto: void textout( BITMAP* bmp, const FONT* f, const char* str, int x, int y, int color); bmp representa el mapa de bits en el cual imprimiremos el texto. str será la cadena de texto a mostrar, x e y serán las coordenadas del punto superior izquierdo del texto (0,0 nos asegura que el texto aparecerá en la esquina de la pantalla). color , como ya se había visto, representa el color con que se mostrará str . Sólo queda introducir la noción de lo que es FONT. Tal como en Windows, Allegro es capaz de mostrar varios tipos de fuentes. Basta con especificar un puntero hacia la fuente deseada, la cual deberá ser del tipo FONT. Veremos en futuros capítulos como cargar fuentes. Afortunadamente, por el momento, podemos usar la predeterminada de la BIOS: extern FONT* font; Normalmente podríamos modificar este puntero hacia cualquier otra fuente, pero esto no nos es muy útil por el momento ya que no sabemos cómo cargarlas. Ahora, ¡podemos mostrar el tamaño de la pantalla! Basta con agregar: #include <string.h> /* incluimos el archivo de cabecera de esta librerí a del C ANSI */ Necesitaremos una variable nueva: //Un pequeño buffer que puede contener 256 caracteres char str_buf[ 256 ]; Transformaremos nuestra función textout : /*Copiamos primero el tamaño de la pantalla en str_ buf */ sprintf(str_buf, “El tamaño de la pantalla es: %d x %d” ,SCREEN_W, SCREEN_H); /* Listo, no nos queda más que reemplazar nuestra c adena de texto */ textout(buffer,font,str_buf, 0, 0,makecol( 150 , 150 , 255) ); ¡Admire el resultado! Obtiene en su pantalla, arriba a la izquierda: “El tamaño de la pantalla es: 640 x 480”. ¡Pero los desarrolladores de Allegro pensaron en todo! Para ahorrarle una línea y una variable, crearon una función textout con la sintaxis de printf . Podemos ahora simplificar nuestro programa: //Cambiaremos el color para celebrar... textprintf(buffer,font, 0, 0,makecol( 100 , 255 , 100 ), ”El tamaño de la pantalla es: %d x %d” ,SCREEN_W,SCREEN_H); void textprintf( BITMAP* bmp, const FONT* f, int x, int y, int color, const char* fmt, ...); Aquí esta el prototipo de la función. Ahora puede imprimir texto con formato, justo como con printf . La función es muy similar a textout , por lo que no hay necesidad de regresar a los detalles. Gracias a esta

Page 9: Tutorial Allegro

implementación podemos deshacernos de la línea include <string.h> y de nuestro buffer de caracteres, el cual es inservible. ¿Por qué no mostrar la posición del disco en la pantalla? ¡Y que mejor que el color varíe con respecto a la posición actual del disco! /* He aquí la línea, un poco más larga */ textprintf(buffer, font, 0, 10, makecol(circle_pos_x / SCREEN_W*255 , circle_pos_x / SCREEN_W*255 , circle_pos_x / SCREEN_W*255 ), "Posicion del disco: %d" , (int )circle_pos_x); La cadena de caracteres se mostrará con una luminosidad progresiva, lo que crea un efecto visual simpático. ¡En los últimos 50 píxeles, el texto desaparece! En efecto, es debido a que la variable circle_pos_x toma un valor superior a 255. El programa “repasa” el excedente desde 0, y se genera un color muy oscuro. Para ir un poco más lejos, existen otras funciones muy similares a textprintf : void textprintf_centre( BITMAP *bmp, const FONT *f, int x, int y, int color, const char *fmt, ...); Esta función realiza exactamente lo mismo que textprintf , excepto que interpreta a x e y como el centro de la cadena de caracteres a mostrar, y no la esquina superior izquierda como solía ser. void textprintf_right( BITMAP *bmp, const FONT *f, int x, int y, int color, const char *fmt, ...); Aquí es lo mismo, pero x e y son las coordenadas de la esquina superior derecha. void textprintf_justify( BITMAP *bmp, const FONT *f, int x1, int x2, int y, int diff, int color, const char *fmt, ...); Aquí dibujamos el texto de manera justificada entre las coordenadas x1 y x2 . Si las letras llegaran a salirse la función se convierte en un textprintf estándar. ¡Listo!, ya conoce todas las funciones de Allegro basadas en textprintf . Existen otras funciones anexas que pueden ser muy practicas en algunos casos. Por ejemplo: int text_length( const FONT *f, const char *str); Esta función devuelve la longitud en píxeles, de la cadena de caracteres str usando la fuente f . Puede servir para saber si un determinado texto se saldrá de la ventana, por ejemplo. int text_height( const FONT *f); Aquí no debemos especificar ninguna cadena de caracteres, pues sólo nos interesa la altura. Solo basta con definir la fuente f, y saber así su altura en píxeles. Ahora utilizaremos la función textprintf_centre en nuestro pequeño programa de prueba. Reemplazaremos, pues, la línea que escribe las dimensiones de la pantalla por ésta otra: /* Mostramos las dimensiones justo en el centro de la pantalla */ textprintf_centre(buffer, font, SCREEN_W/ 2, SCREEN_H/ 2, makecol( 100 , 255 , 100), “El tamaño de la pantalla es: %d x %d” , SCREEN_W, SCREEN_H); Compile y ejecute el programa… Si siguió correctamente mis instrucciones, el disco debería mostrarse por encima del texto. Es perfectamente lógico, pues se dibuja el disco después de haber impreso el texto en pantalla. Ahora, posicione esta línea después de la que dibuja el disco. Vuelva a compilar y a ejecutar. Esta vez, el texto si se mostrará por encima del disco, pero, ¡Está en un curioso cuadro negro! Le aseguro… esto es perfectamente normal. Arreglémoslo inmediatamente con las siguientes lineas:

Page 10: Tutorial Allegro

/* Pasamos al modo transparente de impresión de tex to */ text_mode( -1 ); /* Mostramos las dimensiones justo en el centro de la pantalla */ textprintf_centre(buffer, font, SCREEN_W/ 2, SCREEN_H/ 2, makecol( 100 , 255 , 100), “El tamaño de la pantalla es: %d x %d” , SCREEN_W, SCREEN_H); El resultado es perfecto: ¡No más cuadro negro! ¿Pero como funciona? Pues, todo texto impreso se divide en dos partes: la frontal (el texto en sí) y la parte trasera, que es un cuadro rodeando los caracteres de nuestro texto. Por defecto, este cuadro es negro, y es por eso que no se había dado cuenta (planee muy bien la sorpresa, ¿verdad?), pero si el texto se imprime sobre otro color este cuadro es perfectamente visible. Veamos la función text_mode: int text_mode ( int mode); Es muy fácil de utilizar. mode es el color del cuadro sobre el cual se mostrará el texto. Podemos, pues, crearla con makecol , como ya hemos visto. Por ejemplo para mostrar un cuadro blanco por debajo: text_mode(makecol( 255 , 255 , 255 ) ); Hay sin embargo un detalle. Para no mostrar este cuadro, es decir tener un efecto de transparencia, hay que utilizar el color -1 . Pero hay que tener mucho cuidado, ya que text_mode se aplicará a todas las funciones de impresión de texto subsiguientes. Por ende, hay que definir un color de fondo cada vez que le haga falta. Por ejemplo en nuestro programa, a partir de la segunda ocurrencia del ciclo, ¡todos los textos serán mostrados en transparencia! Bueno, ahora tiene todas las herramientas necesarias para mostrar texto en pantalla, durante sus programas. No dude pues en usar todas las funciones, nada vale tanto como la práctica y la experiencia personal. Hemos dibujado un disco… ¿No sería mejor imprimir una imagen de verdad? (Hablamos entonces de un sprite )

4.3 Dibujar sprites en pantalla Es totalmente posible cargar una imagen y mostrarla en cualquier lugar de la pantalla. Es la base misma de un juego 2D. Un sprite no es más que un mapa de bits, como nuestro buffer, excepto que esta vez, hay que inicializar su contenido. Normalmente, no tenemos por que modificar un sprite que ya haya sido cargado a la memoria, solo debe ser dibujado en pantalla, quizá con un modo de dibujado particular (como una transparencia, o utilizando un color dominante). Escoja una linda imagen de tamaño mediano (320 x 200 por ejemplo), o redimensiónela para que quepa en pantalla. La imagen no debe estar guardada en formato de 8 bits por píxel, es un caso especial que estudiaremos más tarde. Modificaremos nuestro programa una vez más. Reemplazaremos el disco por un sprite . Para comenzar, renombraremos la variable circle_pos_x (para más legibilidad). Consideramos que su sprite se llama sprite.bmp y que se encuentra en la misma carpeta que el ejecutable. /* Declaramos la variable que representa la posició n de la imagen. Si programa en C, póngala al inicio de la función main */ double sprite_pos_x = 0; /* Ahora declaramos nuestro sprite, como un puntero hacia un BITMAP. ¡Recuerde que es un puntero, no olvide el asterisco! */ BITMAP* sprite1; /* Ponga estas líneas después de la inicialización del modo video */ sprite1 = load_bitmap( “sprite.bmp” , NULL); /* Ahora verificamos que la imagen haya sido cargad a */ if (!sprite1) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);

Page 11: Tutorial Allegro

allegro_message( “Error. No se pudo leer la imagen” ); return 1; } ¿Por qué cargar la imagen después de la inicialización del modo de video? Pues por que la imagen se cargará de diferente manera dependiendo del modo de video que esté inicializado. Si ejecuta el programa ahora, nada debería de haber cambiado. Si obtiene el error al cargar la imagen, verifique que haya escrito bien su nombre y que la imagen realmente esté en la carpeta correcta. No hay novedades aparte de la función load_bitmap : BITMAP* load_bitmap( const char *filename, RGB *pal); ¡Es una función realmente importante! Para comenzar, devuelve NULL si hay algún error, si no, devuelve un puntero hacia un BITMAP que será cargado a partir de la cadena filename . Como ya sabemos Allegro puede gestionar modos de 8 bits por píxel, pero este modo es particular, pues necesitan el uso de paletas de colores. No nos ocupamos de pal por el momento. Sólo sepa que para cargar una imagen en modo truecolor (esto es 15, 16, 24 ó 32 bits) pasamos NULL como valor de pal . El puntero devuelto apunta hacia una zona de memoria que ha sido creada. No hay que olvidar liberar este espacio antes de salir el programa o cuando ya no necesitamos el sprite. Ya nos ocuparemos de eso… Por el momento, load_bitmap , soporta los archivos bmp, lbm , pcx y tga . Se define el tipo de archivo por su extensión. Los sprites tienen una particularidad y es que poseen un color de máscara (mask color). Cuando dibuja un sprite cualquiera, siempre se dibujará en una forma perfectamente rectangular. Imaginemos por un instante que quiere dibujar una canica roja por encima de un fondo con textura de pasto. La canica roja se almacenará separadamente de la textura del césped. Ciertamente, su archivo de imagen contendrá una canica roja centrada. ¿Pero alrededor? Como es un rectángulo, probablemente el fondo sea negro. Si ejecutamos el juego… ¡horror! ¡La canica no se muestra correctamente sobre el pasto! ¡Se dibuja en un rectángulo (o un cuadrado) negro! Es necesario, de alguna manera, quitar el fondo de la imagen. Es exactamente el mismo problema que para la visualización del texto (espero que se acuerden…). Aquí interviene nuestro color de máscara. En modo truecolor, todos los píxeles conteniendo el color ( 255 , 0, 255) se ignorarán. Sustituya el negro que rodea la canica por este color (fushia) y obtendrán el resultado deseado. Es así de fácil. Ahora, se va a modificar de nuevo nuestro ciclo infinito controlado por la tecla [ESC] . He aquí la nueva versión: /* Pasamos al modo transparente para el dibujado de l texto */ text_mode( - 1); while ( ! key[KEY_ESC]) { // Mientras que no se oprima la tecla ESC /* Iniciamos borrando el buffer */ clear_bitmap(buffer) ; /* Dibujamos el sprite centrado verticalmente e n la pantalla */ draw_sprite(buffer, sprite1, ( int )sprite_pos_x - sprite1 -> w/ 2, SCREEN_H / 2 - sprite1 -> h/ 2 ); /* Mostramos las dimensiones justo en el centro de la pantalla */ textprintf_centre(buffer, font, SCREEN_W / 2, SCREEN_H / 2, makecol( 100 , 255 , 100 ), "Resolucion : %d x %d" , SCREEN_W, SCREEN_H); /* Mostramos las coordenadas del sprite */ textprintf(buffer, font, 0, 10, makecol(sprite_pos_x / SCREEN_W * 255 , sprite_pos_x / SCREEN_W * 255 , sprite_pos_x / SCREEN_W * 255 ), "sprite_x : %d" , ( int ) sprite_pos_x); /* Movemos el sprite 1 píxel a la derecha */ ++ sprite_pos_x ;

Page 12: Tutorial Allegro

/* Si el sprite sale de la pantalla salimos del programa */ if (sprite_pos_x - sprite1 -> w / 2 >= SCREEN_W) break ; /* Ahora copiamos el contenido del buffer a la pantalla */ blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H) ; } Ejecute el nuevo programa... Si el programa va lento, no se preocupe, sólo aumente el valor que sirve para desplazar el sprite hacia la derecha. Coloqué la función text_mode antes del comienzo del ciclo ya que usamos el mismo modo de texto en todo el programa, y es inútil llamar la función sin cesar. No olviden nunca que lo que dibujen primero en el buffer quedará detrás de lo que dibujen después. Ya que nuestro sprite es de tipo BITMAP, podemos recuperar sus dimensiones, lo cual es muy útil para mostrar sprites centrados /* Anchura del sprite */ sprite1->w; / * Altura del sprite */ sprite1->h; Si no comprende porqué el sprite es centrado en el programa, haga un esquema y anote los valores que conocemos (el tamaño de la pantalla y del sprite) y posicione los elementos como si usted fuese la computadora. ¡Veamos el prototipo de la función que nos sirve para mostrar el famoso sprite! void draw_sprite( BITMAP* bmp, BITMAP* sprite, int x, int y); Podemos observar que es bastante simple. bmp representa el destino (el mapa de bits en la cual se dibujará el sprite). sprite es el puntero hacia el sprite (su nombre lo dice todo). x , y indican la posición en píxeles en donde se dibujará el sprite. Esta función va a dejar transparentes todos los píxeles con el color de máscara. Esta función sería equivalente a blit(sprite, bmp, 0, 0, x, y, sprite->w, sprite->h) , excepto que blit copia todos los píxeles sin excepciones ni transparencias. Estas funciones de dibujo pueden acelerarse por hardware si el controlador de vídeo lo permite. La función draw_sprite es la base de todas las demás funciones de visualización de sprites, como lo veremos a continuación. Existen otras maneras de dibujar un sprite. Por ejemplo, ¿Quiere que el sprite se dibuje con un efecto de espejo vertical? ¿U horizontal? ¿O por qué no hay los dos al mismo tiempo? void draw_sprite_v_flip( BITMAP *bmp, BITMAP *sprite, int x, int y); void draw_sprite_h_flip( BITMAP *bmp, BITMAP *sprite, int x, int y); void draw_sprite_vh_flip( BITMAP *bmp, BITMAP *sprite, int x, int y); Estas funciones hacen lo mismo que draw_sprite , excepto que invierten la imagen verticalmente, horizontalmente, o los dos a la vez. Las imágenes son imágenes "espejo" que no es lo mismo que efectuar una simple rotación. La función de rotación existe también, pero sigue siendo mucho más lenta. Hela aquí: void rotate_sprite( BITMAP *bmp, BITMAP *sprite, int x, int y, fixed angle); Como era de esperarse, esta función necesita un argumento suplementario: el ángulo. Pero esta vez, el tipo no es int o float sino fixed , que es un nuevo tipo que define Allegro. No es muy importante que sepamos todo acerca de él, ya lo veremos con el tiempo. Simplemente debe saber que este tipo se almacena en 32 bits. Los primeros 16 para la parte entera y lo último para la parte flotante. Sencillamente, es un int administrado de tal modo que podemos guardar datos con parte fraccionaria (o decimal). Sus valores pueden pues variar entre -32768 y 32767. Veamos como hacer conversiones entre los tipos comunes y el tipo fixed . //Convierte un int a fixed. Es lo mismo que escribi r x<<16. fixed itofix( int x); //Al revés, convierte un fixed a int int fixtoi( fixed x);

Page 13: Tutorial Allegro

//Convierte un float a fixed. fixed ftofix( float x); //Y finalmente, convierte un fixed a float float fixtof( fixed x); Listo, ahora sabe lo esencial de este nuevo tipo para Poder usar la función rotate_sprite . Es importante mecionar que el ángulo se maneja como un número entre 0 y 256. 256 representa entonces una rotación completa (360°), 64 un cuarto de círculo (90°), y a sí sucesivamente... Esta función, al igual que draw_sprite ignorará automáticamente los píxeles caracterizados por el color de máscara, entonces es muy práctica para los juegos donde las superposiciones están siempre presentes, aunque sea relativamente lenta... Divirtámonos un poco, modificando el programa de nuevo para ilustrar las novedades. Hagamos pues que la imagen vaya rotando al mismo tiempo que se desplaza, y el ángulo variará en función de la distancia recorrida desde el inicio. Solamente debemos cambiar la línea que se encarga de la visualización de del sprite: / * Se dibuja el sprite centrado en pantalla y se e fectúa la rotación * / rotate_sprite(buffer, sprite1, ( int )sprite_pos_x - sprite1->w/ 2, SCREEN_H/2 - sprite1->h/ 2, ftofix (sprite_pos_x)); Admire el resultado... ¿Le agrada? Si encuentra que el sprite avanza demasiado rápido, sencillamente modifique la línea que aumenta el valor de sprite_pos_x . Puede permitirse superar el valor 256 , así 257 se convierte en 1, 258 en 2, etc... Sólo hay que saber lo que se hace. Por otra parte, aprovecho para presentarles el macro ABS(x) de Allegro. Este macro permite sustituir al número x por su valor absoluto, y esto, ¡cualquiera que sea su tipo! Muy fácilmente podemos escribir nuestro programa para efectuar una rotación en el otro sentido (válido para la primer vuelta solamente): / * Se dibuja el sprite centrado en pantalla y se e fectúa la rotación * / rotate_sprite(buffer, sprite1, ( int )sprite_pos_x - sprite1->w/ 2, SCREEN_H/2 - sprite1->h/ 2, ftofix(ABS( 256 - sprite_pos_x))); Un último punto muy importante: antes de que finalice su programa, debe liberar la memoria asignada por load_bitmap() , pues esta función copia el contenido del bitmap en RAM. Es necesario liberar este espacio memoria. Para eso, la única función que debe utilizarse, se trata de: void destroy_bitmap( BITMAP *bmp); El final del programa anterior es fácil de escribir ahora: /* Liberamos la memoria del sprite */ destroy_bitmap(sprite1) ; /* Liberamos la memoria del buffer */ destroy_bitmap(buffer) ; /* Fin de la función main */ return 0 ; } END_OF_MAIN();

4.4 Diferentes métodos de dibujado Ha llegado la hora de hablar de métodos de dibujado, ya es tiempo de hablar del tema. Les aseguro que Allegro es tan fácil de utilizar que la gestión de diferentes métodos de dibujo no requiere un talento o conocimiento excepcional. Como ya lo dije, el método que he explicado es ni más ni menos el double buffering. Ahora nos interesaremos en un método más expandido y también más eficiente, el page flipping. También veremos el triple buffering, que cuando es bien manejado da resultados excelentes. El truco consiste en hacer pruebas entre los tres métodos y determinar cuál funciona mejor en su equipo. Aunque hay que tener en mente que la eficiencia de un juego depende de la configuración material de la computadora, la cual, dependiendo del usuario

Page 14: Tutorial Allegro

variará. Así puede que un método funcione mejor que otro, en nuestro equipo, pero no en todos. Desde mi punto de vista, lo mejor es dejar que el usuario tome la decisión final. Siempre lo he hecho así, y es una buena solución para tener a todos contentos. Bueno, hemos hablado suficiente, ¡pasemos ahora a las cosas serias! A continuación veremos como inicializar el page flipping, el cual no tiene nada de complicado.

4.4.1 El page flipping /* Declaramos los dos buffers que vamos a usar y ot ro que apuntará hacia uno de los dos... */ BITMAP* page1, page2, buffer; int current_page = 1; /* Como siempre, inicializamos primero el modo de v ideo */ set_color_depth( 16); if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640 , 480 , 0, 0) != 0) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); return 1; } /* Ahora inicializamos el page flipping... */ page1 = create_video_bitmap(SCREEN_W, SCREEN_H); page2 = create_video_bitmap(SCREEN_W, SCREEN_H); /* Aqui verificamos que los dos buffers se hayan cr eado correctamente */ if (( ! page1) || ( ! page2)) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message ( "Memoria insuficiente para double buffering!" ); return 1; } ¡Y eso es todo para la inicialización! Obviamente explicaré todo esto. Entonces, como siempre, iniciamos el modo de video primero, es obligatorio. Obligatorio también, es verificar el resultado, eso se hace sin decirlo… Un page flipping digno de su nombre, se apoya en dos buffers de video, alojados directamente en la memoria de video, al contrario del otro método que ya vimos, que lo único que hace es crear un buffer en RAM. Esto se hace así para poder obtener una aceleración gráfica y un dibujado directo desde la memoria de video a la pantalla. De hecho la variable screen apunta a un lugar de la memoria de video (no de la RAM). Es de esperarse que haya que usar una función especial para crear un bitmap directamente en la memoria de video. BITMAP* create_video_bitmap( int width, int height); Esta función crea un mapa de bits de anchura width y de altura height . Devuelve un puntero hacia el BITMAP creado. Normalmente esta imagen creada en la memoria no esta completamente vacía (negra) y deben quedar residuos. Hay que vaciarla antes de poder utilizarla. Ya lo ven… no soy nada tonto, pues copié lo que ya había escrito para la función create_bitmap , y esto es así por que la implementación es muy simple. Sin embargo, no todo es color de rosa en la memoria de video. Si bien casi todas las tarjetas gráficas recientes tienen suficiente memoria, hay modelos viejitos que están muy limitados. Además algunos SO tienen dificultad para detectar la capacidad máxima de las tarjetas gráficas (como me pasó con BeOS y una GeForce2MX). Así que hay que andarse con cuidado. Me explicaré: dos buffers de 1024 x 768 px a 32 bits de profundidad, necesitan 6 MB de memoria… claro que puede copiar el máximo número posible de gráficos en su memoria de video, para tener una velocidad superior, pero sepa que grandes incompatibilidades pueden surgir. Por si fuera poco nada asegura que el juego vaya más rápido… podría ir más lento en máquinas viejas. De hecho es necesario que la tarjeta gráfica soporte aceleración por hardware para la copia de memoria de video a la misma memoria de video, si no el page flipping resulta totalmente ineficaz, sin contar el dibujado de sprites… Para resumir, si quiere hacer page flipping, solo debe crear en la memoria de video los dos buffers, creo que es inútil tratar de copiar cualquier otra cosa allí, a menos que tenga buenas razones para hacerlo, claro.

Page 15: Tutorial Allegro

El page flipping se basa en un principio muy sencillo: dibujamos en una hoja y la mostramos, luego dibujamos en la otra y la mostramos, y así seguimos alternando el dibujado entre las dos páginas. ¡Y ya! Aquí vemos como se traduciría si tomamos nuestro ejemplo del disco que se mueve. while (key[KEY_ESC]) { clear_bitmap(buffer); circlefill(buffer, ( int )circle_pos_x, SCREEN_H/ 2, 50, makecol( 255 , 255 , 255 )); /* Movemos el disco a la derecha 0.1 píxel */ circle_pos_x += 0.1 ; /* Esta es la función que dibuja el buffer que tenemos en la memoria video */ show_video_bitmap(buffer); /* Ici, on s'occupe de l'alternance des deux bu ffers */ if (current_page == 1) { current_page = 2; buffer = page2; } else { current_page = 1; buffer = page1; } } ¡Listo! Añada las dos líneas para destruir los dos bitmaps, y por supuesto el END_OF_MAIN(); . Ejecute el programa. ¡Si tienen una tarjeta gráfica que de la talla, debería obtener un resultado claramente más fluido que en el primer ejemplo donde utilizaba el doble buffering! Sólo una función nueva, veamos que es lo que hace exactamente. int show_video_bitmap( BITMAP* bitmap); Mejor romper el suspenso inmediatamente, esta función lleva bien muy su nombre: intenta hacer un "page flip" del screen hacia el bitmap pasado en parámetro. Este bitmap debe tener exactamente el tamaño de la pantalla y debe haber sido creado por create_video_bitmap si no grandes problemas puede haber. Esta función devuelve cero si todo salió bien, si no cualquier otro valor. Ven, nada demasiado complicado aún. Sepa que show_video_bitmap(buffer) es el equivalente de blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H) para el page flipping, con las restricciones comentadas. Es buen momento de abordar el problema de la sincronización vertical aquí... Ah, le veo perplejo, voy a contarle todo eso. Normalmente, la tarjeta vídeo dibuja lo que contiene en una determinada dirección – en este caso a la variable global screen - a una determinada frecuencia. Por ejemplo, si copia una pantalla completa roja sobre el bitmap screen , si su pantalla está configurada en 75Hz, su tarjeta vídeo va a enviar exactamente 75 veces su contenido, a la dirección de screen (a la pantalla). De paso, más la tasa de refresco es elevado, mejor es la comodidad para sus ojos. Es aquí que llegan las complicaciones. Si el programa no espera que la tarjeta vídeo termine de dibujar su imagen sobre la pantalla, el contenido de screen va a cambiar justo en medio del dibujado sobre la pantalla (por supuesto, es el juego que seguirá dibujando sus imágenes sin interrupción). Si este fenómeno se produce todo el tiempo se obtienen pequeños desgarres en la imagen, veremos la imagen dividida en bandas. Ahora bien, la función show_video_bitmap debe esperar pacientemente que la tarjeta vídeo termine de dibujar antes de cambiar el contenido de la memoria vídeo. Pueden hacer la prueba más adelante en el curso: indique el número de fotogramas por segundo (FPS) de su juego en alguna parte de la pantalla: ¡será idéntico a la tasa de refresco del monitor! Por supuesto es necesario que la computadora sea bastante potente, ya que si este no el caso, este índice va a desplomarse y habremos fracasado ya que el programa no podrá seguir el ritmo impuesto por la tasa de refresco del monitor. Volvamos de nuevo al double buffering: la función final que nos sirve para copiar el contenido del buffer sobre la pantalla se burla completamente del estado del dibujado y modifica la memoria de vídeo sin ningún escrúpulo. Pero existe una función que corrige eso.

Page 16: Tutorial Allegro

void vsync( void ); Esta función espera a que la imagen sobre el monitor esté dibujada completamente. Al final de este dibujado, el haz de electrones pasa de abajo a la derecha del monitor a su posición inicial, o sea arriba a la izquierda. Durante este lapso de tiempo, la tarjeta vídeo no envía nada a la pantalla puesto que esta última no puede hacer nada sino regresar de nuevo para dibujar la próxima imagen. Es aqui que eso pasa a ser interesante para usted: Para el método double buffering simple, intente colocar un vsync() exactamente antes de llamar la función que copia el resultado final a la pantalla. Y aquí siempre es la misma cosa, si su compu es rápida, la limitación efectuada va a permitirle obtener tantos FPS como el valor de la tasa de refresco del monitor. Si la compu es demasiado lenta, misma cosa, los FPS se derrumban. Es por eso que le aconsejo proponer siempre distintos métodos de dibujado en su juego... He terminado la parte sobre el double buffering y el page flipping. Diviertase haciendo pruebas, incluso con los juegos ya programados que vienen con Allegro que proponen distintos métodos de dibujado (como el demo oficial). ¡Una vez más, no dude a utilizar el page flipping, es a menudo muy eficaz y muy expandido!

4.4.2 El triple buffering Podríamos preguntarnos de que nos sirve el triple buffering, si el page flipping parece funcionar muy bien. Quizá sienta que está en un curso de tecnicismos dedicados a desorientar al usuario, así que para no complicarlo más, el triple buffering es, a grandes rasgos, lo mismo que el page flipping, exepto que usamos tres páginas de memoria en vez de dos. Evidentemente, es aún más goloso en memoria gáfica, pero si es soportado da también resultados excelentes. Afortunadamente Allegro tiene lo necesario para verificar si este método es soportado por la tarjeta gráfica. No tengo nada más que decir en el apartado de “generalidades” así que vamos a lo nuestro. ¿Ok? /* Declaramos los tres buffers que vamos a usar y u n puntero que apuntará a alguno de los tres... */ BITMAP* page1, page2, page3, buffer; int current_page = 1; set_color_depth( 16); /* Como siempre, iniciamos primero el modo video */ if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640 , 480 , 0, 0) != 0) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); return 1; } if ( ! (gfx_capabilities & GFX_CAN_TRIPLE_BUFFER)) enable_triple_buffer(); if ( ! (gfx_capabilities & GFX_CAN_TRIPLE_BUFFER)) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message( "Triple buffering no soportado!" ); return 1; } /* Ahora iniciamos el triple buffering... */ page1 = create_video_bitmap(SCREEN_W, SCREEN_H); page2 = create_video_bitmap(SCREEN_W, SCREEN_H); page3 = create_video_bitmap(SCREEN_W, SCREEN_H); /* Aqui verificamos que las tres páginas estén crea das */ if (( ! page1) || ( ! page2) || ( ! page3)) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message ( "Memoria i nsuf iciente para triple buffering!" ); return 1; }

Page 17: Tutorial Allegro

Si todo esto le parece confuso y claro a la vez, es normal. Primero que nada hay que asegurarnos que la tarjeta gráfica soporta el triple buffering, para lo cual contamos con una variable global llamada gfx_capabilities . int gfx_capabilities; Esta variable es usada para almacenar muchos flags (marcas, banderas). Cada flag, indica si la tarjeta gráfica es capaz o no de realizar cierta operación, dependiendo del valor del bit que corresponda a la operación (si es 1 funciona, si no es 0). Aquí mencionaré sólo dos, pues sería inútil mencionar cada uno de ellos, pero en la ayuda oficial puede averiguar más. GFX_HW_VRAM_BLIT Este indica si la tarjeta gráfica es capaz de acelerar por hardware el dibujado directo desde la memoria de video. Si es el caso, ¡bingo!, podrá disfrutar de todos los beneficios del page flipping. GFX_CAN_TRIPLE_BUFFER Este es el flag que nos interesa ahora. Si está activo (1) es perfecto, si no, aún queda una última oportunidad… la función enable_triple_buffer . int enable_triple_buffer( void ); Perdón por ser aguafiestas pero esta función sólo es eficaz bajo algunas condiciones… Aparentemente es muy eficaz en DOS, pero en Windows nunca la he visto funcionar… Bueno, se supone que hace lo posible para que se autorice el triple buffering. Devuelve 0 si el triple buffering se estableció con éxito. Es todo lo que hay que saber. Entonces, siguiendo el código, miramos si el triple buffering está activado. Si no es así probamos suerte con esta función y hacemos una última verificación. Si todo va bien, podemos pasar al alojamiento de los tres buffers y verificar que hayan sido creados correctamente. ¡Listo! La inicialización está hecha. Ahora mostramos nuestro clásico disco: while (key[KEY_ESC]) { clear_bitmap(buffer); circlefill(buffer, ( int )circle_pos_x, SCREEN_H/ 2, 50, makecol( 255 , 255 , 255 )); /* Desplazamos el disco 0.1 pixel */ circle_pos_x += 0.1 ; /* Esta es la función que muestra el bitmap alo jado en la memoria gráfica */ do { } while (poll_scroll()); /* Hacemos la solicitud */ request_video_bitmap(buffer); /* Aquí nos ocupamos de la alternancia de los b uffers */ if (current_page == 1) { current_page = 2; buffer = page2; } else if (current_page == 2) { current_page = 3; buffer = page3; } else { current_page = 1; buffer = page1; } }

Page 18: Tutorial Allegro

Pues ahí está el código, y como podrá constatar es el mismo principio base del page flipping, pero hay nuevas funciones que es necesario explicar. int request_video_bitmap( BITMAP *bitmap); Esta función solo debería ser utilizada en el triple buffering. La función hace una solicitud de “page flip" (cambio de página) al bitmap especificado. Al contrario de las otras funciones, ésta devuelve el control al programa inmediatamente. int poll_scroll( void ); Esta función también es exclusiva del triple buffering. Para explicarlo sencillo, verifica que la función de antes haya terminado su trabajo. Devuelve cero si podemos llamar request_video_bitmap de nuevo, si no devuelve otro valor… En otras palabras, dibujamos en una página. Luego, solicitamos que la página se muestre con la función request_video_bitmap , mientras dibujamos en la segunda página y esperamos a que la primera se termine de mostrar. Cuando termina, hacemos un request_video_bitmap sobre la segunda página y dibujamos sobre la tercera, esperando a que la primera “esté lista de nuevo”.

4.4.3 Combinar varios métodos de dibujado en un mis mo programa

Y es aquí donde saco un conejo del sombrero, pues vamos a hacer cohabitar los tres métodos de dibujado en un mismo y único programa. De hecho, vamos a hacer tres funciones, la primera inicializará el modo gráfico en función del método elegido. La segunda será llamada antes de dibujar el juego sobre el buffer. Y la última será utilizada para mostrar el buffer en pantalla. No tiene más que crear la función de dibujo, la cual dibujará en un buffer, independientemente del método elegido, lo cual es muy interesante. Pero primero hay que hacer algunos define y unas variables externas para facilitarnos la vida, además de los prototipos de las funciones. #define DOUBLE_BUFFERING 1 #define PAGE_FLIPPING 2 #define TRIPLE_BUFFERING 3 BITMAP* page1, page2, page3, buffer; int draw_method, current_page; /* La función que copia el buffer en pantalla */ void buffer_onto_screen( void ); /* La función que prepara el buffer entes de dibuja r */ void prepare_drawing( void ); /* La función que inicializa el modo de video */ int init_video( int draw_method, int size_x, int size_y, int color_depth, int WINDOWED_MODE); Comencemos con el pedazo más grande: init_video . No es una función oficial de Allegro, así que no la presentaré como las otras. Esta función tomará como parámetro el método de dibujado draw_method , que tiene tres posibles valores: DOUBLE_BUFFERING, PAGE_FLIPPING o TRIPLE_BUFFERING. También le pasamos la resolución de pantalla elegida size_x y size_y , la profundidad de color color_depth que puede valer 8, 15, 16, 24 ó 32. Y finalmente WINDOWED_MODE es el parámetro que determina si queremos utilizar una ventana pantalla completa. Esta variable puede valer TRUE o FALSE (estas constantes están definidas en allegro.h). Note que pude haber definido dos constantes FULLSCREEN y WINDOWED y llamar una u otra función

Page 19: Tutorial Allegro

dependiendo del valor… Pero hay que tomar una decisión, y de paso admito que soy más bien perezoso. Listo, ahora que sabe exactamente que hace cada parámetro, va a escribir la función. Tranquilo que aquí estoy yo para darle el código. int init_video( int config_draw_method, int size_x, int size_y, int color_depth, int WINDOWED_MODE) { int gfx_mode; if (WINDOWED_MODE == TRUE) { gfx_mode = GFX_AUTODETECT_WINDOWED; color_depth = desktop_color_depth(); /* Si su juego debe ser truecolor pero su s u escritorio está en 256 colores, forzamos los 16 bits */ if (color_depth < 16) color_depth = 16; } else gfx_mode = GFX_AUTODETECT_FULLSCREEN; /* Aqui podemos aplicar el color_depth tranquil amente */ set_color_depth(color_depth); Haremos una pequeña pausa aquí… Debo explicarle unas cosas sobre desktop_color_depth(); int desktop_color_depth(); Esta función devuelve la profundidad de color utilizada por el escritorio actual, desde donde se lanzó el programa. Claro está que esta función es interesante sólo si se ejecuta el programa desde un entorno gráfico de ventanas. ¿Por qué? Pues sencillamente por que una aplicación irá mucho más rápido si utiliza la misma paleta de colores que la del escritorio. Si no, se pondrán en marcha conversiones adicionales para convertir los colores del dibujo de la ventana a los colores del escritorio, lo que puede traducirse en lentitud. Y créame, es más rápido hacer una ventana de 32 bits que una de 16 bits cuando el escritorio es de 32 bits. Esta función devuelve 0 si no es capaz de determinar el resultado, o sencillamente si no existe tal (por ejemplo en DOS) Antes de continuar quiero señalarle que haremos una función inteligente: si ve que no se puede hacer page flipping o triple buffering, va a resignarse a usar double buffering, el cual es casi seguro que funcione en todas partes. if (set_gfx_mode(gfx_mode, resol_x, resol_y, 0, 0) != 0) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message( "%s" , allegro_error); return FALSE; /* FALSE significa error, claro */ } // Aquí no hay nada nuevo, verificamos si se pu ede hacer triple buffering // Lo que vimos unas páginas arriba if (config_draw_method == TRIPLE_BUFFERING) enable_triple_buffer(); if (gfx_capabilities & GFX_CAN_TRIPLE_BUFFER && config_draw_method == TRIPLE_BUFFERING) draw_method = TRIPLE_BUFFER; else if ( ! (gfx_capabilities & GFX_CAN_TRIPLE_BUFFER) && config_draw_method == TRIPLE_BUFFERING) draw_method = DOUBLE_BUFFERING; else draw_method = config_draw_method;

Page 20: Tutorial Allegro

// Si ya pasamos toas las pruebas para el tripl e buffering // Todo debería ir sin sorpresas if (draw_method == TRIPLE_BUFFERING) { page1 = create_video_bitmap(SCREEN_W, SCREEN_H); page2 = create_video_bitmap(SCREEN_W, SCREEN_H); page3 = create_video_bitmap(SCREEN_W, SCREEN_H); if (( ! page1) || ( ! page2) || ( ! page3)) { //Destruimos los bitmaps, pues no serán utilizados destroy_bitmap(page1); destroy_bitmap(page2); destroy_bitmap(page3); draw_method = DOUBLE_BUFFERING; // Pasamos al modo por default } } if (draw_method == PAGE_FLIPPING) { page1 = create_video_bitmap(SCREEN_W, SCREEN_H); page2 = create_video_bitmap(SCREEN_W, SCREEN_H); if (( ! page1) || (!page2)) { //Destruimos los bitmaps, pues no serán utilizados destroy_bitmap(page1); destroy_bitmap(page2); draw_method = DOUBLE_BUFFERING; // Pasamos al modo por default } } if (draw_method == DOUBLE_BUFFERING) { buffer = create_bitmap(SCREEN_W, SCREEN_H); if ( ! buffer) { allegro_message( "No se pudo iniciar el modo gráfico" ); return FALSE; } } /* Aquí todo fué bien */ return TRUE; } Ya tenemos nuestra función. Recordemos que devuelve TRUE cuando todo fue bien y FALSE cuando algo salió mal, así que la podemos usar en nuestro programa de la siguiente manera: if (init_video(PAGE_FLIPPING, 800 , 600 , 16, TRUE) == FALSE) { //Hubo un error, debemos salir. return 0; } // Aquí todo va bien Y ya tenemos lista la inicialización. No es tan difícil como pudiera parecer. Ahora nos enfocaremos en la función que prepara el buffer para que podamos escribir en él, y hablo de la función prepare_drawing . Decidimos que no reciba ningún argumento, ni devuelve nada. void prepare_drawing( void ) { if (draw_method == TRIPLE_BUFFERING) { if (current_page == 0) { buffer = page2; current_page = 1; }

Page 21: Tutorial Allegro

else if (current_page == 1) { buffer = page3; current_page = 2; } else { buffer = page1; current_page = 0; } } else if (draw_method == PAGE_FLIPPING) { if (current_page == 2) { buffer = page1; current_page = 1; } else { buffer = page2; current_page = 2; } } if (draw_method != DOUBLE_BUFFERING) acquire_bitmap(buffer); return ; } Y eso es todo para la preparación del buffer, en el cual dibujaremos más adelante. Como podrá darse cuenta, no hay casi nada nuevo pues todo ha sido visto anteriormente. Sin embargo espero que haya notado la función acquire_bitmap . No perdamos tiempo y descubramos de qué se trata. void acquire_bitmap( BITMAP *bmp); Esta function bloquea el bitmap bmp antes de dibujar en él. Sólo funciona con los bitmaps creados por create_video_bitmap , y además sólo es útil en ciertas plataformas. Por ejemplo Windows debe utilizarla, pero DOS no. Y en estos momentos debe estar preguntándose ¿Por qué no habló de esto antes? Si parece ser algo muy importante. Y de hecho lo es, pero tengo mis razones. Cada vez que usó blit u otras funciones de dibujado, en ocasiones anteriores, acquire_bitmap se ejecutó sola, pero bloquear una superficie DirectDraw es muy lento, así que en vez de bloquear y desbloquear 50 veces para dibujar 50 cosas, bloqueamos el buffer antes de dibujar cualquier cosa en él y lo desbloqueamos una vez que se ha dibujado todo lo necesario. Es precisamente lo que hacemos en nuestra función, cuando preparamos el buffer aprovechamos para bloquear el buffer actual. Pero tenga mucho cuidado, recuerde siempre desbloquear el buffer al finalizar el dibujado, pues los programas DirectX no pueden recibir ninguna señal de entrada tipo teclado o ratón, es decir que no pueden hacer nada que no sea dibujar mientras el buffer está bloqueado. Veamos la función que hace lo contrario. void release_bitmap( BITMAP *bmp); Desbloquea bmp, el cual normalmente es bloqueado por acquire_bitmap . Si por azar ha bloqueado un bitmap varias veces debe desbloquearlo el mismo número de veces. Un último pequeño esfuerzo, la función que mostrará el buffer completamente dibujado en pantalla. La llamaremos buffer_onto_screen . No recibe argumentos ni devuelve nada, justo como la anterior. void buffer_onto_screen( void ) { if (draw_method != DOUBLE_BUFFERING) release_bitmap(buffer);

Page 22: Tutorial Allegro

if (draw_method == TRIPLE_BUFFERING) { /* Hay que asegurarnos que el último flip s e llevó a cabo */ do { } while (poll_scroll()); /* Solicitamos que el buffer sea mostrado * / request_video_bitmap(buffer); } else if (draw_method == PAGE_FLIPPING) show_video_bitmap(buffer); else if (draw_method == DOUBLE_BUFFER) blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); return ; } Si todo lo anterior le pareció poco difícil, entonces es una buena señal. Y acabo de explicar con emoción la primera cosa que quise que apareciera en este tutorial, pero como podrá notar me fui desviando para explicar otras cosas… espero que no le moleste. Para celebrar todo esto, veamos un pequeño programa de ejemplo. if (init_video(PAGE_FLIPPING, 800 , 600 , 16, TRUE) == FALSE) { /* Error general. Hay que salir */ return 0; } /* Aqui todo va bien ! */ circle_pos_x = 0; while (key[KEY_ESC]) { prepare_drawing(); /* Aqui comienza la sección de dibujado persona l. En este caso usaré el ejemplo del circulo, pues ya lo conocemos */ clear_bitmap(buffer); circlefill(buffer, ( int )circle_pos_x, SCREEN_H/ 2, 50, makecol( 255 , 255 , 255 )); /* Movemos el círculo a la derecha 0.1 pixel */ circle_pos_x += 0.1 ; /* Esta es la función que mostrará el buffer en pantalla */ buffer_onto_screen(); } ¡Esto es grande! Ahora puede elegir un método de dibujado, sin tener que ocuparse de las funciones que hacen el dibujado en sí, y de paso el código se hace más sencillo. Sólo se ocupará de la variable buffer , que apunta directamente al buffer que nos interesa. No dude en hacer muchas pruebas, cambiando los modos y la resolución, y sobre todo incluyendo su propia secuencia de dibujado. [Continuará…]