Академический Документы
Профессиональный Документы
Культура Документы
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”
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.
Muy importante, antes de hacer lo que sea con la librería, hay que llamar la función de inicialización.
¡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:
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.
Ahora pasaremos al modo gráfico propiamente dicho. Para ello utilizaremos la siguiente función:
set_gfx_mode(GFX_AUTODETECT,640,480,0,0);
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:
if(set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
/*Como han seguido bien mis explicaciones, han de saber que allegro_message se
utiliza únicamente en modo texto, es por eso que utilizamos 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.
Aquí vemos un aspecto importante de la librería, la gestión de teclas oprimidas por el usuario.
if(key[KEY_ENTER])
printf(“La tecla Enter está oprimida \n”);
if(key[KEY_SPACE])
printf(“La barra espaciadora está oprimida \n”);
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!
4. Gráficos en pantalla
¡No se preocupe! Aclararemos todo esto… circlefill es la función que nos permitirá mostrar un disco en
pantalla.
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):
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:
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);
Esta función puede ser acelerada por hardware, dando como resultado un rendimiento increíble.
circle_pos_x += 0.1;
//Movemos el círculo a la derecha 0.1 píxeles
/* 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;
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, BITMAP* 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:
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.
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:
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.
¡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:
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:
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:
/* Pasamos al modo transparente de impresión de texto */
text_mode(-1);
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:
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)
/* Ahora declaramos nuestro sprite, como un puntero hacia un BITMAP. ¡Recuerde que
es un puntero, no olvide el asterisco! */
BITMAP* sprite1;
¿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:
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:
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
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!
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?
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:
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):
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:
¡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.
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 buffers */
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.
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.
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!
/* Declaramos los tres buffers que vamos a usar y un puntero que apuntará a alguno
de los tres... */
BITMAP* page1, page2, page3, buffer;
int current_page = 1;
set_color_depth(16);
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);
/* Hacemos la solicitud */
request_video_bitmap(buffer);
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”.
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
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
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.
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.
else
draw_method = config_draw_method;
// Si ya pasamos toas las pruebas para el triple 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;
}
}
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:
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;
}
else if (current_page == 1) {
buffer = page3;
current_page = 2;
}
else {
buffer = page1;
current_page = 0;
}
}
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.
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);
if (draw_method == TRIPLE_BUFFERING) {
/* Hay que asegurarnos que el último flip se llevó a cabo */
do {
} while (poll_scroll());
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.
¡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á…]