Allegro Tutorials: / Comments Will Be in A Characteristic Blue-Green
Allegro Tutorials: / Comments Will Be in A Characteristic Blue-Green
Tutorials
Introduction
First, I must tell you that a general knowledge of C/C++ is required, and that Allegro
must be correctly installed (we’ll look at this point in another tutorial.)
- Throughout all my tutorials, function prototypes belonging to allegro will be
written in bold characters.
- /*Comments will be in a characteristic blue-green*/
- Types are in maroon( int, float, …), as well as types specific toAllegro
(BITMAP, DATAFILE, ...).
- Control structures in blue ( if, else, while, {, }, …)
- Numbers in red (0 , 1, 324, 4, …)
- “include” and “define” in green.
- "Character strings in gray"
1 . What is Allegro?
Let’s get directly to the subject: “What is Allegro?” It’s a library that furnishes everything
you need to program a video game. Allegro gives you a solution for managing the screen,
sound, keyboard, mouse, timers, in short, everything you need. Allegro was originally
written by Shawn Hargreaves for the Atari ST, and soon after ported to DOS. The first
versions of the library are dated at the start of 1996, so the library isn’t all that young!
Programmers (contributors) to Allegro quickly developed it into a multi-platform library.
Today you can use Allegro under DOS (DJGPP, Wacom), Windows (MSVC, Wingw32,
Cygwin, Borland), Linux (console) Unix (X), BeOS, QNX, and MacOS (MPW) . You
can see that the great power of Allegro comes from the fact that it is supported by a large
number of operating systems. Specifically, you can port and compile your programmes
under any type of compiler (mentioned above) without changing a single line of code. In
each case, Allegro will select only the drivers appropriate for that OS. For example, a
program compiled under Windows uses DirectDraw accelaration, while under Linux you
can use the X11 drivers: again a small bonus, you can take advantage of the graphic
acceleration from your video card in 2D, and that s not a negligible difference. This is the
same for DirectSound and DirectInput in Windows. On the other hand, 3D isn’t the
strong point of this library: it won’t take advantage of the acceleration provided by
Direct3D. Be assured, however, that OpenGL is very well supported, thanks to
AllegroGL, an Allegro add-on. We also note that this library is free (gratis) and free (as
in free speech), since the source code is available!
It’s very important to call the initialization function before doing anything else with the
library:
Perfect! Allegro is initialized! And now, if you want to use the keyboard and at would be
very practical...
Now things get more interesting: if the function has failed, it returns -1; if it succeeds, it
returns the number of buttons on your mouse that Allegro can manage. It is thus
important to test this result because not all DOS users will have a working mouse
installed. You can thus write:
This function uses the same format as the printf function. It is thus very convenient
to use. Note: This function should be used only in non-graphic display modes! Clearly,
you can’t use it unless you haven’t yet set a video mode or if you’ve explicitly changed to
text mode. For example, here, you haven’t yet initialized the video mode, so you can use
the function without any problem.In OS where you have a text mode in a “console”, like
DOS or Unix, the message will dispaly normally in the console. For Windows (or for
BeOS), this will produce a dialogue box appropriate for that OS with an “OK” button at
the bottom. The dialog’s title will be the name of the program that’s running. This
functionis therefore very practical for signaling errors independent of the OS.
Now, set the graphics mode itself. To do this, you use the command:
But wait, how on earth do you use this function? Here’s its prototype:
int set_gfx_mode(int card, int width, int height, int v_width, int v_height);
First, the easy part. The function returns 0 if it succeeds, otherwise it returns a negative
number.
“int card” isn’t what it seems like; it’s the index of the graphic mode that you want to
use. Here, then, are the different values that you can set: (the #define is part of allegro.h):
-GFX_AUTODETECT . This lets Allegro choose the best driver. It will set
window mode if the resolution isn’t available to full screen and if the OS supports
it, of course.
-GFX_AUTODETECT_FULLSCREEN . Forces Allegro to choose a full-
screen mode.
-GFX_AUTODETECT_WINDOWED . Forces Allegro to choose a window
mode.
-GFX_TEXT . This is very useful for returning to text mode. In this case, you can
use a 0 for the screen dimensions (this is just for readability).
There are naturally other values, but they are more specific to each OS (and thus you
should avoid using them). We’ll lok at this later, but for now let’s stick to the basics. The
values width and height represent the size of the graphic screen created. You can find the
dimension of the screen by using the macros SCREEN_W (width) and SCREEN_H
(height), which this function initializes. At the moment, don’t worry about v_width and
v_height (you can use zero for both these values). As every respectable program does,
it must check to see that there was no error! Here are the necessary tests:
Voilà! We’ve finished initializing our little program! We could add sound and
joystick control, but that’s the subject of later chapters. Now, let’s make the program stop
when you press the ESC key. Thus, add the following few lines of code...
Here’s an important aspect of the library: handling keypresses from the user.
It’s very simple:all the keys in Allegro are grouped in this table (of size KEY_MAX). It
is a table where each key has its own index number. If you want to see the entire list of
keys, open the file allegro/keyboard.h; everything is defined there. Normally this table
represents the physical state of the actual keyboard. That is to say, whether it is pressed or
not. Thus, this table is meant to be read-only. It is “modified” only by keyboard
interrupts. You can certainly simulate a keypress, but that’s another story... Here are other
examples of use (don’t put these into your program; it won’t work there! That’s because
printf doesn’t work in graphic mode):
if (key[KEY_ENTER]) {
printf("The ENTER key was pressed!\n");
}
if (!key[KEY_SPACE]) {
printf("The space bar was *NOT* pressed!\n");
}
You will have noticed that Windows applications have their entry point as WinMain
and not main. Because of this the macro END_OF_MAIN() is required: it let you use
4. Display
Now let’s talk about display, without a doubt the most important part of the library.
To do this, we will display a white disc with a diameter of 50 pixels which will be move
towards the right side of the screen. It will be exactly at the vertical center of the screen.
The user will have to touch [ENTER] to display the disc. We’ll modify the loop to that
waits to see if the [ESC] key has been pressed:
/* while ESC has not been pressed and we are still on screen */
while (!key[KEY_ESC] && circle_pos_x - 50 < SCREEN_W) {
Don’t worry; we’ll explain all of this. The circlefill function serves to display the disc
on the screen.
x and y represent the coordinates of the center of the circle on the screen. All the
coordinates are given with respect to the upper left corner of the screen. radius
represents the radius in pixels. As you may have noticed, there’s a new data type here, the
BITMAP. We’ll discuss that soon.
Just remember that screen is a global variable that points to a BITMAP (an image
area) that represents video memory. It points directly to the screen and the size of that
bitmap is thus SCREEN_W * SCREEN_H.
bmp is thus the bitmap upon which we will draw our disc. It’s the destination of the
drawing function.color is the color, as its name indicates. The best way to specify a color
is to use a utility function:
This function lets you define a color independent of the color mode. You can call this
function in 8, 15, 16, 24, and 32 bit per pixel mode. r represents the red component, g the
green component, and b the blue component. This is also called an RGB color format.
These components can have a value from 0 to 255 inclusive. makecol(255,255,255)
specifies the color white in the current color mode.
Now, if you start the program, you’ll see a horrible blinking of the screen. You won’t
see a disc, but rather a succession of black and white lines. This is absolutely not the
deisred effect! Well then, where’s the problem? It’s very simple. To program a game
correctly, one must never write directly to the screen unless [se fiche totalment] the
result, but that’s not the case here. Instead, we must use a new method of display, called
double buffering. The principle is very simple: instead of drawing to the screen, one
draws to a bitmap that has been [prealablement] placed in RAM. Then, at the end of our
[boucle], we copy the contents of the bitmap (also called a buffer) to the screen in one fell
swoop. This is not the method that gives the most spectacular results, but it will always
give a much cleaner [ce sera deja nettement mieux] display.
This is a portion of the definition of a BITMAP (you can see the whole definition in
the library headers):
There are other variables in the BITMAP type, but you won’t need them. Thus, to
find the size of the screen, you can look at screen->w, which represents the width of the
screen and screen->h, which represents the height. Don’t forget that Allegro works only
with pointers to BITMAP. (BITMAP *) We declare our video buffer like this:
Now we have to create a bitmap that will have the same dimensions as the screen.
You must do this after you initialize the video mode.
This function creates a bitmap of the given width and height. It returns a pointer to
the bitmap that was created. Normally this image is crated in RAM and isn’t completely
empty (black) You have to clear it up from residual bits before using it. To clear a bitmap
to black, use this function:
This function can be accelerated, and when it is, the result will be an incredible gain
in performance.
/* while ESC has not been pressed and we are still on screen */
while (!key[KEY_ESC] && circle_pos_x - 50 < SCREEN_W) {
/* Begin by clearing the buffer */
clear_bitmap(buffer);
We have replaced screen with buffer in the appropriate functions. It remains only to
find out what the function blit does… This function lets you copy a part of a source
bitmap to a part of a destination bitmap.
The disk’s speed depends on the speed of your computer (meaning that display is a
very limiting factor in the speed of a program). Even with a super-powerful computer,
you can’t attain astronomical speeds (in terms of images per second) bedcause you have
to wait for the video card to finish drawing each image! In the next sections, we’ll discuss
real time, that is to say, that your game runs at the same speed, no matter what the power
of the computer on which it runs.
/* while ESC has not been pressed and we are still on screen */
while (!key[KEY_ESC] && circle_pos_x - 50 < SCREEN_W) {
bmp represents the bitmap on which the text will be displayed. str is the character
string to display, x and y are the coordinates of the upper left point where the text will be
displayed (0 and 0 assure us that the text will be at the corner of the screen). We need
only introduce the idea of a FONT. As with Windows, Allegro can display many kinds
of typefaces. You just have to specify a pointer to the desired font, which must be of type
FONT. To load fonts, you must reference a database. This will be described in later
chapters. Luckily, you can use the default BIOS font, expressed thus:
By the way, you can modify this pointer to point to any other font, but tat’s not very
useful at the moment, because we don’t know how to load fonts yet.
Now you can display the size of the screen on the screen. Just add this:
Admire the result! You will see at the upper left of the screen: “Here is the screen size:
640*480”. But the creators of Allegro thought of everything. To save one line, they let
Notice the prototype. You can now display formatted text, just as you do with printf.
The function is similar to textout, so we don’t have to review those details here. One final
note: using textprintf allows you to take out the line “#include <string.h>”, don’t forget to
also take out the declaration of the character buffer, which is no longer used.
Why not display the circle’s position on the screen? And make the color change
depending upon the position...
Thus, the character string will display with increasing brightness, which creates an
interesting visual effect. In the last fifty pixels, the string “disappears.” In fact, this is
because the variable circle_pos_x has taken a value greater than 255. The program cuts
this off to zero, which is a very dark color. However, to continue, there are other
functions which are similar to textprintf.
This function does exactly the same thing as textprintf, except that it interprets x and
y as the center of the character string rather than the upper left corner.
Same thing, except that x and y are the coordinates of the upper right corner.
void textprintf_justify(BITMAP *bmp, const FONT *f, int x1, int x2,
int y,int diff, int color, const char *fmt, ...);
Here, the text is justified between the x1 and x2 coordinates. If the letters overlap, the
Run the program... If you've followed our instructions correctly, the circle should
display above the text. This is logical because you draw the circle after drawing the text,
so the disk is superimposed. Now, move the line that draws the text so that it follows the
line that draws the circle.
Re-run the program. You will see this time that the text lies “above” the circle, but it
seems to be written on some strange black box! Let me reassure you right away that this
is perfectly normal. Let's modify this right away. To do this, here are the new lines to
write:
The result is truly perfect: no more black framework! But how can this be? All
displayed text is composed of two parts: the foreground, which is the text itself, and the
background, which represents a box enclosing the displayed character string. By default,
this box is black, and it is for this reason that you didn't see it (have I managed to
[magouillé mon coup pas vrai?]), but on the contrary, you can see the text clearly if it’s
drawn on a color other than the color of the “box.” Let's look at the text_mode function:
This function is very easy to use. mode is the clor of the box on which the text is
displayed. You can create it with makecol, as you have seen. For example, to display on
a white box:
text_mode(makecol( 255, 255, 255));
There’s one small exception. If you don’t want to draw the “box,” that is, you want to
have the effect of transparency, you must use the color -1. Important: once you use
text_mode to set a mode, that modeis in effect for all subsequent text display! Thus, you
must redefine the background color each time you need to change it. For example, in our
program, from the second occurrence of the loop onwards, all text will be drawn with a
transparent background!
You now have all the tools in hand to display text on the screen from your programs.
Do not hestitate to try all the functions; nothing beats practice and personal experience to
thoroughly learn how to use them.
We’ve drawn a circle...wouldn’t it be better to display a true image (i.e., a sprite)?
Why do you place the image loading code after the video mode initialization? So that
the image can be loaded in the correct (initalized) video mode! If you start the program
now, nothing will have changed. If your image didn’t load correctly, check the spelling of
the file name string, and make sure that the file is really in the correct directory. There’s
nothing remarkable about the load_bitmap function:
This is truly an important function! First, it returns NULL in case of an error; if not, it
returns a pointer to a BITMAP which has been created from the given filename
string.As you already know, Allegro works in an eight bit per pixel color mode, which is
/* Check if the sprite is off the screen; if so, exit the program */
if (sprite_pos_x - sprite1->w / 2 >= SCREEN_W) {
break;
}
Run our new program... If the program is slow, adjust the value that serves to move
the sprite to the right. There’s nothing really new there; it’s just the time to make a point
about the program [en cours]. I have placed the text_mode function before the loop
because the program works entirely in transparent mode. Thus,it’s inefficient to call the
function every time you go through the loop. Always remmeber that what you draw into
the buffer first is in the “back,” and the order of objects is determined by the order in
which the program draws them. Given that our sprite is of type BITMAP, you can easily
dtermine its size, which is very practical when you want to display the sprite centered:
/* sprite width */
sprite1->w;
/* sprite height */
sprite1->h;
If you don’t understand yet why the sprite is centered, draw a diagram and label all
the values you know (screen size, sprite size) and it will become much clearer. Let’s take
a closer look at the function that displays the famous sprite!
void draw_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y);
It’s quite basic. bmp represents the destination (the image where the sprite will be
drawn). sprite is the pointer to the sprite (the name is quite appropriate). x and y are
simply the coordinates of the upper left corner where the sprite will be drawn. This
function will ignore all pixels that have the mask color. This function is similar to
“blit(sprite, bmp, 0, 0, x, y, sprite->w, sprite->h)”, except that blit copies all the pixels.
These drawing functions can be accelerated greatly if the video driver allows it. The
draw_sprite is the basis for all other sprite display functions, as we will see.
There’s another way to draw a sprite. For example, do you want to draw the sprite
with a vertical flip (a vertical mirror effect) or a horizontal flip, or maybe both at the
same time?
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);
Notice that, as you might have suspected, this function needs an extra argument: the
angle. Only this time, it’s not a an int or float, but a fixed. fixed is a new type defined by
Allegro. It’s not important to know all about it right now; we’ll see the details later.
Simply note that this type is encoded in 32 bits. The first 16 bits are the integer part, and
the last 16 bits are for the decimal part. Actually, this type is an int managed so that it can
hold fractions. The values can vary from -32768 to 32767, but what we really need to
know is how to convert between common types and the fixed type.
Voilà; now you know the esentials of this new type that is used by the rotate_sprite
function. We must remind you that the angle is in the range 0 to 256. 256 represents a
complete rotation, 64 a fourth of a circle, and so on… This function, like draw_sprite,
will automatically skip pixles marked by the mask color. It is thus very practical for
games where there are always objects superimposed on other objects, even though it is
relatively slow...
Let's amuse ourselves one more time by modifying the program to illustrate these
features. We will rotate the image at the same time that it moves, and the angle will very
as a function of the distance from the starting point. You just have to change the line that
draws our sprite:
/* Draw the sprite at the vertical center of the screen and rotate */
rotate_sprite(buffer, sprite1,
(int)sprite_pos_x – sprite1->w/2,
SCREEN_H/2 - sprite1->h/2, ftofix(sprite_pos_x));
/* Draw the sprite at the vertical center of the screen and rotate */
rotate_sprite(buffer, sprite1
(int)sprite_pos_x- sprite1->w/2,
SCREEN_H/2 - sprite1->h/2,
ftofix(ABS(256 - sprite_pos_x)));
One last point, and it’s extremely important: before exiting your program, you
absolutely must free the memory allocated by load_bitmap(). Because this function
copies the bitmap contents into RAM, you must free that memory space. To do this, use
this function:
It has been quite some time ago that you asked me to talk about different methods of
display, and now let’s talk about it! Let me assure you that Allegro is so simple to use
that managing the different display modes doesn’t require any particular proficiency. As I
have already mentioned, the method I’ve showed you is nothing more or less than double
buffering. Let’s now investiagte more [ répendu ] mode which also gives better
set_color_depth(16);
And that does it for initialization. Now, the explanation. At the beginning, as usual, we
have to set the video mode; that’s required. It goes without saying that we also need to
verify the result... Page flipping gets its name from the two video buffers placed directly
into video memory, as opposed to the mode we already saw that creates a single buffer in
RAM. Creating two pages in vdieo memory has the benefit of giving acceleration by a
direct display of video memory into the same memory! [ vers cette même mémoire!]
Remember that the variable screen points to an area of video memory! We must now use
This function creates a bitmap of the given width and height. It returns a pointer to
the bitmap that was created. Normally, this image in video memory isn’t completely
empty (blank); it contains whatever was left over in memory. It is thus necessary to erase
it before using it. [Vous voyez, je me suis pas foulé, j'ai recopié ce que j'avais déjà écrit
pour la fonction] As you can see, I’ve just copied and pasted what I wrote for the function
create_bitmap, so the modification is simple... However, video memory is not a bed of
roses. Even though recent video cards have ample memory, there are still lots of video
cards with limited capacity. Moreover, some OS don’t correctly detect the amount of
available video memory with certain video cards—for example BeOS with a
GeForce2MX—which makes this a very delicate procedure to use. Let me explain: two
buffers that are 1024 by 768 by 32 bits take 3 megabytes of video memory... Yes,you can
always try to copy the maximum number of graphics for your game into video memory
with the goal of getting maximum speed, but [sachez] that this can give rise to big
incompatibilities. Moreover, I don’t believe that the results are always guaranteed; that is
to say that the game could be slower on certain old configurations. In fact, it is necessary
for a video card to support video memory to video memory copy in hardware; if not, page
flipping is totally ineffective for more than blitting sprites. To sumarize, you have to be
sure that two buffers have been created in video memory if you want to do page flipping.
I think it’s useless to try to copy what’s in one buffer into the other [de copier quoi que ce
soit d'autre dedans], unless you have good, and well-understood, reasons for doing so.
Page flipping is based on a very simple principle: you draw one on page, display that
page, then draw on the other and display it, and so on... In effect, you alternate the
drawing of the game between two pages; that’s all! Here’s how we write the example of
the circle that moves from left to right, using page flipping:
while (key[KEY_ESC]) {
clear_bitmap(buffer);
And it’s done! Add the two lines to destroy the two bitmaps, and, of course
END_OF_MAIN();. Run the program. If you have a configuration that fits the bill, you
should obtain a result that’s cleaner and less “jumpy” than the first result example that
used double buffering! There’s only one new function here, and here’s exactly what it
does:
To spoil the suspense all at once, this function is very well named: it tries to do a “page
flip” of screen to the bitmap passed as a parameter. This bitmap must be exactly the size
of the screen and should have been created by create_video_bitmap; otherwise, you’ll
have trouble with scaling. As usual, this function returns zero if successful, non-zero if
not. As you see, it’s very simple. Please note that show_video_bitmap(buffer) is the
equivalent of blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H) for page
flipping with the aforementioned restrictions. This is a good place to talk abot the
problem of vertical synchornization... Ah, I see that you are confused by this; let me
explain.
Ordinarily, the video card displays its contents at a particular address--which is what the
global variable screen contains-- at a particular frequency. For example, let’s say you
copy a completely red screen to the screen bitmap. If your screen is set to 75HZ, your
video card will send the contents of the screen address to the display exactly 75 times
each second. (By the way, the higher this “refresh rate,” the easier it is for your eyes.)
This is where the complications occur. If the program doesn’t wait for the video card to
finish drawing its image on the screen, the contents of the screen bitmap will change
while the drawing takes place—your gamewill certainly try to display its images
continuously. This phenomenon happens all the time, and it explains why you the image
seems to “tear” slightly whie scrolling. In the example of the moving circle, it’s very
noticeable. However, the show_video_bitmap function waits paitently for the video
card to finish drawing before changing the contents of the video memory that is about to
be displayed! You can test this later: count the number of images per second that you
display some part of your game: it will be the same as the screen’s refresh rate! This
means that the computer has to be powerful, because if it isn’t, your program will be
overwhelmed because it can’t keep up with the “cadence” imposed by the monitor’s
refresh rate. Coming back to double buffering, the function that serves to copy the buffer
contents to the screen will draw to video memory without regard to the refresh rate.
This function waits until the image is completely drawn on the monitor. At the end of
the drawing, the electron beam moves from the lower right of the screen to its beginning
position (the upper left). During this period, the video card will not send anything to the
screen since it can’t do anything while the electron beam moves back to draw the next
image. This is what we are interested in:
For simple double buffering, try putting a vsync() just before calling the function that
“blit”s the final image to the screen. Again, as always, if your compute ris fast, this lets
you obtain as many FPS (Frames Per Second, or images per second) as the refresh rate of
your screen. If the computer is too slow, same thing, performance will suffer. This is why
I recommend that you always allow peopleto select different display modes in your game.
That concludes the discussion about double buffering and page flipping. Experiment
with existing Allegro programs and games that let you choosen different display methods
(like the official demo). Again, don’t hesitate to use page flipping; it is often very
effective and speedy.
/* Declare three page buffers and a pointer that will point to one of the other
two. */
BITMAP* page1, page2, page3, buffer;
int current_page = 1;
set_color_depth(16);
If this all seems clear and obscure at the same time, that’s perfectly normal. First, you
have to make sure that the video card supports triple buffering. This is done with a global
variable named gfx_capabilities :
int gfx_capabilities;
This variable is used with many flags. Each flag describes if the video card has a
certain capability or not according to whether the designated bit is on or not (if it’s 1, the
capability exists, otherwise it’s zero.) Chaque flag décrit si la carte vidéo est capable ou
non d'effectuer certaines taches, suivant si le bit qu'il désigne est armé ou non (bon, si
c'est 1 c'est que c'est bon, sinon, c'est 0). I won’t go over every bit here because there are
lots of them, and it would be useless to try to cover them all (see
allegro/docs/html/allegro.html for the full story). However, here are some of themost
useful ones:
- GFX_HW_VRAM_BLIT : tells if the video card can significantly speed up
drawing by means of video memory. If so, bingo—you can experience the joys of page
flipping.
- GFX_CAN_TRIPLE_BUFFER : this is the flag we’re interested in right now. If
it’s on, that’s perfect; if not, there’s one last chance: the enable_triple_buffer function.
int enable_triple_buffer(void);
I’m sorry to spoil the mood, but this function doesn’t work under certain conditions. It
appears to work under DOS, but under Windows I’ve never gotten it to work. sous DOS
mais sous Windows, je ne l'ai jamais vu marcher... In any event, it tells if triple-buffering
while (key[KEY_ESC]) {
clear_bitmap(buffer);
/* Post a request */
request_video_bitmap(buffer);
This is a good thing indeed. [Bien, voilà une bonne chose de faîte] ! To prove it, the
general principle is the same for changing the pages; let’s explain the new functions:
La je sors le grand jeu, puisque nous allons faire cohabiter les trois modes
d'affichage dans un même et unique programme ! En fait, je vais faire trois fonctions ici.
La première initialisera le mode graphique en fonction de la méthode choisie. La
deuxième sera appelée avant de dessiner le jeu sur le buffer. Et la dernière sera utilisée
pour coller le buffer à l'écran. Vous n'aurez donc plus qu'à créer la fonction de dessin, qui
affichera sur un buffer, independamment de la méthode choisie, ce qui est très intéressant
!
Mais tout d'abors, définissons quelques defines et quelques variables externes
pour nous faciliter la vie. Et bien sur on crée les prototypes des trois fonctions. Rien ne
vous empèche d'ailleurs de créer un fichier exprès pour l'affichage en déclarant les
BITMAPS* comme locaux à ce fichier. C'est d'ailleurs plus cohérent mais faîtes comme
vous voulez du moment que ça marche.
#define DOUBLE_BUFFERING 1
#define PAGE_FLIPPING 2
#define TRIPLE_BUFFERING 3
Allez, on commence par le plus gros morceau : init_video. Ce n'est pas du tout
une fonction d'Allegro officielle puisque ça va être la notre donc je ne la présente pas
comme les autres. Reprenons, cette fonction prend en paramètre la méthode d'affichage
draw_method, qui a donc le droit de valoir DOUBLE_BUFFERING,
PAGE_FLIPPING ou TRIPLE_BUFFERING. On lui passe aussi la résolution de
l'écran choisi (size_x * size_y), la profondeur de la palette de couleur color_depth qui
peut donc valoir 8, 15, 16, 24 ou 32. Et en enfin WINDOWED_MODE est le paramètre
qui détermine si l'on veut ou non utilser un mode fenetre ou un mode plein écran. Cette
variable a le droit de valoir TRUE ou FALSE (ce sont deux noms definis dans allegro.h).
Notez que j'aurais pu définir deux defines par exemple FULLSCREEN et WINDOWED
et envoyer un ou l'autre à la fonction... Mais bon, il faut bien faire un choix et sur le coup,
j'avoue que je suis plutot feignant... Allez, maintenant que vous savez exactement son
rôle, vous allez l'écrire ! Heh, rassurez-vous, je suis là pour vous la donner :
if (WINDOWED_MODE == TRUE) {
gfx_mode = GFX_AUTODETECT_WINDOWED;
color_depth = desktop_color_depth();
/* Si votre jeu est en true color mais que le bureau est en 256
couleurs, forcez le 16 bits quand même*/
if (color_depth < 16)
color_depth = 16;
}
else
gfx_mode = GFX_AUTODETECT_FULLSCREEN;
Alors, on va faire une petite pause ici... Il faut que je vous explique le coup du
desktop_color_depth();
intdesktop_color_depth();
Cette fonction retourne la profondeur de la palette de couleur utilisée par le bureau actuel
d'où est lancé le programme. Alors bien sur, cette fonction n'est interessante que si elle
est appelée à partir d'une fonction en mode fenetré. Pourquoi donc? Et bien, tout
if (config_draw_method == TRIPLE_BUFFERING)
enable_triple_buffer();
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))
draw_method = DOUBLE_BUFFERING; /* on passe au
mode par défaut */
}
if (draw_method == PAGE_FLIPPING) {
if (draw_method == DOUBLE_BUFFER) {
buffer = create_bitmap(SCREEN_W, SCREEN_H);
if(!buffer) {
allegro_message("Impossible de créer un buffer de
(%d*%d)", SCREEN_W, SCREEN_H);
return FALSE;
}
}
Alors, je vous préviens tout de suite, il faut faire attention avec cette fonction : en
effet, si jamais l'initialisation du triple buffering échoue à la création du troisième buffer
vidéo, vous allez continuer avec du double buffering et avec deux pages de mémoires
vidéo allouées qui ne seront jamais libérées. Je vous présente vraiment cette fonction
pour vous montrer le principe. Vous devriez avertir l'utilisateur en lui précisant de
changer le mode vidéo au plus vite par exemple. Ce n'est pas très dur à faire, c'est juste
l'histoire de quelques allegro_message. Je vous apprendrais plus tard comment on peut
très bien se sortir de ce genre genre de situations en utilisant des "config files". Cette
fonction retourne donc TRUE si tout s'est bien passé, sinon FALSE. Voilà comment on
pourrait l'appeler par exemple depuis la partie initialisation de votre programme :
Et voilà, la partie d'initialisation est terminée... C'était vraiment pas très dûr n'est-
ce pas? Alors, maintenant, on va s'intéresser à la fonction qui prépare le buffer à être
déssiné ! On va appeler cette fonction prepare_drawing, et on décide qu'elle ne recoie
pas d'argument et ne renvoie rien.
void prepare_drawing(void) {
if (draw_method == TRIPLE_BUFFER) {
return;
}
Et c'est tout bon pour la préparation au dessin d'une nouvelle image! Comme vous
pouvez le constater, il n'y a rien de bien nouveau en fait, car tout a déjà était vu
précédemment. Nouveauté cependant, j'espère que vous l'avez vue, c'est la fonction
acquire_bitmap! Ne perdons pas de temps et allons découvrir à quoi elle sert :
Dernier petit effort, la fonction qui affiche le buffer complètement dessiné sur
l'écran : on la nomme buffer_onto_screen. Elle ne prend et ne donne rien, tout comme la
précédente.
void buffer_onto_screen(void) {
if (draw_method != DOUBLE_BUFFER)
release_bitmap(buffer);
if (draw_method == TRIPLE_BUFFER) {
/* Il faut être sur que le dernier flip a bien eu lieu */
do {
} while (poll_scroll());
return;
}
Si vous ne trouvez pas ça difficile ou que vous n'êtes pas surpris de la tête de la
fonction, alors c'est bon signe ! Eh bien, c'est avec une émotion certaine que je viens de
terminer la première chose que je voulais faire apparaître dans mon tutorial. mais comme
vous pu le constater, il a largement dévié pour devenir le plus généraliste possible à
propos d'Allegro, et j'espère que vous n'allez pas vous en plaindre ! Pour fêter tout ça, on
regarde un petit exemple d'application :
circle_pos_x = 0;
while (key[KEY_ESC]) {
prepare_drawing();
/* Ici, c'est la fonction qui affiche notre bitmap qui est placé en
mémoire vidéo */
buffer_onto_screen();
/* Déclaration de la palette */
RGB* ma_palette;
Une palette est donc en fait un simple tableau contenant 256 éléments RGB. Le
fait de passer ce pointeur en argument de la fonction va mettre la palette du sprite dans
ma_palette! Ainsi, pour utiliser la palette du sprite “sprite.bmp” (n’oubliez pas qu’il doit
s’agir d’une image enregistrée en 8 bits!), on tapera la commande:
BITMAP* sprite1;
Il faut maintenant dire au programme qu’on veut utiliser ma_palette en tant que
palette courante. Oui, je dis bien palette courante car un programme ne peut utiliser
qu’une seule palette à la fois pour dessiner. Inutile d’essayer d’alterner différentes
palettes pendant l’affichage, le resultat est inexploitable, à moins bien sûr d'utiliser des
techniques spécifiques… Mais il vaut mieux passer en mode truecolor si on a vraiment
besion de plus de 256 couleurs. Vous pouvez toutefois essayer pour vous en rendre
compte. Pour définir la palette courante à utiliser, on utilise la fonction:
/* Déclaration de la palette */
RGB* ma_palette;
/* Du sprite */
BITMAP* sprite1;
/* Du buffer vidéo */
BITMAP* buffer;
/* Initialise le clavier */
install_keyboard();
Vous pouvez à présent utiliser les fonctions classiques pour afficher vos bitmaps
telles que blit, draw_sprite et tous ses dérivés. Je dois quand même vous signaler
quelques restictions au niveau de makecol. Cette fonction était très pratique pour définir
une coleur en mode truecolor, mais elle s'avère plus délicate à utiliser en mode 8 bits. Elle
marchera mais vous aurez des resultats plus ou moins probants selon la palette utilisée.
Le mieux est d'entrer directement l'index de la couleur au lieu d'appeler makecol. Par
exemple, si vous voulez faire un rectangle “plein”, vous n'avez qu'à utiliser la fonction
suivante (c'est l'occasion de découvrir une nouvelle fonction...):
void rectfill(BITMAP* bmp, int x1, int y1, int x2, int y2, int color);
Mais maintenant, si votre palette contient la couleur noir à l'index 58, faites
simplement:
6 . La souris
There’s no need to divide this chapter into a sub-chapter, because using the mouse is
so simple! We’ve already seen at the beginning of this tutorial how to initialize the
mouse. Now, let’s see how to use it. But to begin, I would like to refresh your memory
be repeating the code for mouse initialization here:
And it’s done. install_mouse is an Allegro function which returns the number of
available mouse buttons. That is to say, most likely it will return 3. Other mice will return
This isn’t terribly confusing: mouse_x represents the current horizontal coordinate of
your mouse; mouse_y the current vertical coordinate. Remember that these are relative to
the upper left corner of your screen. Thus, the pixel at that corner has coordinates (0,0).
You can find out the location of the mouse pointer at any time; it’s at coordinates
(mouse_x, mouse_y). Thus, it has a range from (0,0) to (SCREEN_W, SCREEN_H).
Ah, but you ask, how can I refresh the values of these variables? Just as with the
keyboard, you don’t have to; Allegro does it all by itself. Strictly speaking, you don’t
have to do anything to be sure that your program knows the place where the user (that is,
the player) has positioned the mouse. In regard to the variable mouse_z, that represents
the position of the mouse scroll wheel, if there is one. When you call init_mouse(),
mouse_z is set to 0 by default. If you scroll with the mouse, the value will be
incremented or decremented. The mouse_b variable represents the other essential aspect
of the mouse: this variable reflects the state of the mouse buttons. Thanks to this
variable, you’ll know if the user is in the process of clicking the left, right, or middle
button. Bit 0 is the left button, bit 1 is the right button, and bit 2 is the middle button.
You will soon see all of this in practice: creating a program that initializes the mouse, the
video mode, loads a sprite into memory and creates a buffer the size of the screen. Once
this is displayed, you shouldn’t have any further programs. Now we will rewrite the main
loop, using the mouse instead of the keyboard. For the sprite, try to select a small image
for the mouse pointer; it will be cleaner than working with a simple image that represents
a wall!
After this loop, dont’ forget to free your sprite et le buffer. This is the moment of truth:
compile and run the program. If you click the left button, your sprite should appear at the
coordinates of your mouse! On the other hand, if you don’t click, you’ll just see a black
screen. It’s easy to quit the program; just click the right button. Simple, isn’t it? There’s
another method to display the mouse automatically. Personally, though, I won’t describe
it because I find it to be “useless.” Finally, let’s say that the method I’ve shown you to
display the mouse gives you copmlete control and is more transparent than the other
method. In effect, the fact of displaying within the main loop [avec tout le reste] lets you
do whatever you want as simply as possible: You need only change the sprite to another
sprite to display a different cursor. If you feel the need to offset the mouse to the left or
right, nothing stops you from doing this:
It’s also very easy to mask the cursor when you want to. You can create animated
cursors more easily this way... You’ll see how to do this easily later on, specifically by
using timers. There are other useful functions that can save you effort for certain types of
games:
This function measusres the distance that the mouse has moved since the last call to
the same function. That is, to say, if the user tries to move too far to the right off the
screen, this function can detect such a movement. Note that because it detects a
displacement, not a position, it can be very practical for games that need constant mouse
movement. You pass two variables as parameters; the function will modify them to
contain the new values.
I think you now know everything essential for working with your mouse. There are some
8. Timers
8.1 Introduction - Theory (simplified)
There are some absolutely indispensable utilities, and when the time arrives that you
decide one day to make a decent game, you simply can’t not use them...Why? Put
simply, it seems that up to now, the speed of execution of all the examples (or games?)
that you have been able to program depend directly on the speed of the processor and
graphics card on which they run! Evidently, this seems to be the first really problematic
aspect. How can one handle this? In fact, you should think about it this way: how can
we be sure that the speed of the final result will be the same on all machines? Clearly,
you can’t depend on anything whatsoever belonging to the computer itself; that’s certain
death. And programmers have for a long time have found the answer: it’s sufficient to
use as a base something that all machines have and that is sufficiently reliable for what
you want to do: the CPU’s internal clock. This is terribly oversimplified, but sufficient to
understand a little how it all works. I am going to talk about things that are less hardware
oriented, but nevertheless quite important for us.
You’ve all noticed something until now: a game is more fluid if it has a greater fps
(Frames Per Second). You’ve also noticed that the FPS are higher if you set up a few
things. Really, it’s quite simple: up until now the reasoning has been as follows: If the
computer is faster, the game is faster. One could note that by proceeding thus, one obtains
a frame rate
remarquer qu'en procédant ainsi, on obtient un framerate (= taux de fps) à peu
près dépendant de ce qu'on veut afficher. Si par exemple on affiche une trentaine de
sprites sur l'écran, le fait de passer à une centaine de sprites va à coup sûr couler le
framerate. Mais comme la vitesse du jeu est directement proportionnelle au framerate, on
aura une vitesse plus faible! Alors là, j'en entends d'ici quelques uns s'étonner de ce que
je viens de dire. C'est pourtant la stricte vérité! Je m'en défends donc à l'instant. Voilà la
structure d'un jeu telle que l'on a fait jusqu'à présent en pseudo code maison:
Si une de ces trois étapes n'a pas une durée constante : le jeu va se dérouler à
une vitesse non constante! Il est clair que ce qui fait perdre le plus de temps au
Alors là, je vous sens perplexes... Pourquoi est-ce que diable ce truc marcherait?
Imaginez-vous un peu... compteur vaut 0, on rentre dans la boucle principale... On
affiche le jeu dans ses valeurs initiales. Mettons qu'il faille exactement 20ms pour les
afficher : compteur vaut maintenant 20. Puisqu'il est positif, on va effectuer à la suite 1)
et 2) exactement 20 fois (puisque le temps mis pour les executer est considéré ici comme
négligeable). Si maintenant d'aventure 40ms sont nécessaires pour afficher le jeu : eh
bien on va effectuer 40 fois de suite 1) et 2). Pour faire plus clair : s'il se trouve que c'est
deux fois plus long d'afficher le jeu, on va déplacer les éléments deux fois plus! Si l'on
compte bien : en 1 seconde on va effectuer 1000 fois 1) et 2), et ce quelque soit le temps
mis pour afficher les images! On a atteint notre but! Bon, si vous n'avez pas compris,
prenez papier-crayon et représentez-vous calmement ce qui se passe dans la boucle à
partir des valeurs initiales...
Il existe toutefois un petit risque... Si le temps nécéssaire pour réaliser 1) et 2)
dépasse la milliseconde, on se retrouve dans une impasse : les images ne pourront jamais
s'afficher. En effet : si compteur vaut 0, et qu'il vaut 2 à la sortie de 1) et 2), il repasse à 1
juste avant de refaire une boucle (une ligne décrémente le compteur). La condition
compteur>0 est toujours vraie, et le jeu ne peut jamais s'afficher.
Si par contre le phénomène n'arrive qu'une seule fois, l'utilisateur final ne vera
Et là tout d'un coup votre programme doit gérer de gros calculs et tout
devient différent:
8.2 Pratique
Alors, il va falloir faire des déclarations que vous n'avez pas encore vues... Elles
sont spécifiques à Allegro et assurent sa compatibilité multi-plateforme. Je me doute que
vous devez prendre la nouvelle de la façon : "ah zut, c'est pas aussi simple qu'il ne le
prétend! Il m'a bien eu!". Rassurez-vous je vais juste vous expliquer ce dont vous avez
besoin et vous allez voir, ça va très bien se passer...
/* on déclare la variable */
volatile int game_time;
LOCK_FUNCTION(game_timer);
LOCK_VARIABLE(game_time);
Ca y est, vous avez initialisé votre compteur! C'était pas vraiment difficile n'est-
ce pas? Maintenant reste la dernière étape : lancer le compteur que l'on a crée. Pour cela,
une simple fonction:
LOCK_VARIABLE(game_time);
LOCK_FUNCTION(game_timer);
Eh bien je crois que vous savez tout maintenant! Essayez d'inclure vous même la
structure que je vous ai décrite au paragraphe d'introduction avec le système qui déplace
tout seul un sprite sur l'écan en fonction de l'appui sur les touches fléchées... Votre
programme tournera en temps réél! Si vous séchez, c'est à dire si une étape vous manque,
regardez la solution complète ci-dessous. Ne la regardez que si vous bloquez, c'est de
loin la meilleure façon d'assimiler!
/* Prototypes de fonctions */
/* Cette fonction initie les timers. Elle retourne 0 si tout s'est bien passé */
int init_timer(void);
int init_timer(void) {
install_timer();
/* Résolution : 1 ms */
if (install_int(game_timer, 1) < 0)
return 1;
/* Résolution : 1 s */
if (install_int(game_sec_timer, 1000) < 0)
return 1;
sec_counter = 0;
game_time = 0;
LOCK_VARIABLE(game_time);
LOCK_VARIABLE(sec_counter);
LOCK_FUNCTION(game_timer);
LOCK_FUNCTION(game_sec_timer);
return 0;
}
int main() {
BITMAP* le_sprite;
/* on va faire du simple double buffering ici */
BITMAP* buffer;
allegro_init();
install_keyboard();
if (init_timer() != 0) {
allegro_message("Erreur lors de l'initialisation des timers!");
return 1;
}
set_color_depth(16);
if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640,480, 0, 0) !=
0) {
allegro_message("Impossible d'initialiser le mode vidéo!");
return 1;
}
--game_time;
}
if (sec_counter != last_sec_counter) {
fps = current_fps;
last_sec_counter = sec_counter;
current_fps = 0;
}
current_fps++;
/* Maintenant on déssine */
/* acquire_bitmap bitmap est inutile ici : on ne dessine qu'un sprite!
*/
clear_bitmap(buffer);
destroy_bitmap(le_sprite);
destroy_bitmap(buffer);
return 0;
}
END_OF_MAIN();
De toute façon, vous pouvez toujours vous reporter au fichier “allegro.htm” pour
en savoir plus long sur le sujet. Avec ce que vous savez maintenant, vous devriez être
tout à fait capable d’écrire des jeux simples.
Version en Rich Text Format / PDF uniquement pour l'instant, à cause des trop
nombreux problèmes de mise en forme que j'ai rencontrés avec l'HTML...
A signaler quelques bugs de mise en forme en PDF (ceci est du à la mauvaise
lecture du RTF par OpenOffice)
Vous pouvez peut-être trouver une version plus récente de ce tutorial sur:
http://iteme.free.fr
Au 28 Juillet 2004,
Tutorial écrit par Emeric Poupon (ITM) .