FLI is an animation format developed by Autodesk for creating and playing computer-generated animations at high resolutions using Autodesk Animator, while the FLC format was the standard format used in Autodesk Animator Pro. These two formats (FLI and FLC) are both referred to as the FLIC format. The original FLI format was limited to a resolution of 320 × 200, while FLC provided higher resolutions and file compression. This chapter focuses on the functions built into Allegro for reading and playing FLIC movies, which are especially useful as cut-scenes within a game or as the opening video often presented as a game begins.
Here is a breakdown of the major topics in this chapter:
Animated or rendered movies are often used in games to fill in a cut scene at a specified point in the game or to tell a story as the game starts. Of course, you can use an animation for any purpose within a game using Allegro's built-in support for FLI loading and playback (both from memory and from disk file). The only limitation is that you can only play one FLI at a time. If you need multiple animations to run at the same time, I recommend converting the FLI file to one or more bitmap images and treating the movie as an animated sprite—although I'll leave implementation of that concept up to you. (First you would need to convert the FLI to individual bitmap images.)
The easiest way to play an FLI animation file with Allegro is by using the play_fli
function, which simply plays an FLI or FLC file directly to the screen or to another destination bitmap.
int play_fli(const char *filename, BITMAP *bmp, int loop, int (*callback)());
The first parameter is the FLI/FLC file to play; the second parameter is the destination bitmap where you would like the animation to play; and the third parameter, loop
, determines whether the animation is looped at the end (1 is looped, 0 is not). In practice, however, you will want to intercept playback in the callback function and pass a return value of 1 from the callback to stop playback.
As you can see from the function definition, play_fli
supports a callback function. The purpose for this is so that your game can continue running while the FLI is played; otherwise, playback would run without interruption. The callback function is very simple—it returns an int
but accepts no parameters.
When you are playing back an animation file, keep in mind that play_fli
draws each frame at the upper-left corner of the destination bitmap (which is usually the screen). If you want more control over the playback of an FLI, you can tell play_fli
to draw the frames on a memory bitmap and then draw that bitmap to the screen yourself. (See the following section on using the callback function.)
The callback function makes it possible to do other things inside your program after each frame of the animation is displayed. Note that you should return from the callback function as quickly as possible or the playback timing will be off. When you want to use a callback function, simply declare a function like this:
int fli_callback(void) { }
You can then use play_fli
to start playback of an FLI file, including the fli_callback
function.
play_fli("particles.fli", screen, 1, fli_callback);
The play_fli
function is not really very useful if you don't also use the callback function. I have written a test program called PlayFlick that demonstrates how to use play_fli
along with the callback to play an animation with logistical information printed after each frame of the FLI is displayed on the screen. Figure 19.1 shows the output from the PlayFlick program.
If you are writing this program from scratch (as follows), you will of course need an FLI file to use for testing. You can copy one of the FLI files off the CD-ROM from the folder for this chapter and project, chapter19playflick. The sample file is called particles.fli, and there are several other sample FLI files in other project folders for this chapter.
#include <stdio.h> #include "allegro.h" #define WHITE makecol(255,255,255) int fli_callback(void) { //display some info after each frame textprintf_ex(screen, font, 0, 0, WHITE,0, "FLI resolution: %d x %d", fli_bitmap->w, fli_bitmap->h); textprintf_ex(screen, font, 0, 10, WHITE,0, "Current frame: %2d", fli_frame); //ESC key stops animation if (keypressed()) return 1; else return 0; } int main(void) { //initialize Allegro allegro_init(); set_color_depth(16); set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); install_timer(); install_keyboard(); //play fli with callback play_fli("particles.fli", screen, 1, fli_callback); //time to leave allegro_exit(); return 0; } END_OF_MAIN()
Allegro provides you with a way to play a raw FLI file that has been mass copied from disk into memory with header and all. The play_memory_fli
function will play a memory FLI as if it were a disk file. The FLI routines must still work with only one file at a time, even if that file was loaded into a memory block (which you must create with malloc
and read into memory using your own file input code). You would also use this function when you have stored an FLI inside a datafile. (For more information about datafiles, refer to Chapter 18.)
int play_memory_fli(const void *fli_data, BITMAP *bmp, int loop, int (*callback)());
The two functions covered thus far were designed for simple FLI playback with little to no control over the frames inside the animation. Fortunately, Allegro provides a low-level interface for FLI playback, allowing you to read an FLI file and manipulate it frame by frame, adjusting the palette and blitting the frame to the screen manually.
To open an FLI file for low-level playback, you'll use the open_fli
function.
int open_fli(const char *filename);
If you are using a data file (or you have loaded an entire FLI file into memory byte for byte), you'll use the open_memory_fli
function to open it for low-level access.
int open_memory_fli(const void *fli_data);
If the file was opened successfully, a value of FLI_OK
will be returned; otherwise, FLI_ERROR
will be returned by these functions. Information about the current FLI is held in global variables, so you can only have one animation open at a time.
The FLI routines make use of interrupts, so you must install the timer by calling install_timer
at the start of the program.
After you have finished playing an FLI animation, you can close the file by calling close_fli
.
void close_fli();
After you have opened the FLI file, you are ready to begin handling the low-level processing of the animation playback. Allegro provides a number of functions and global variables for dealing with each animation frame; you'll see that they are easy to use in practice.
For starters, take a look at the next_fli_frame
function.
int next_fli_frame(int loop);
This function reads the next frame of the current animation file. If loop
is set, the player will cycle when playback reaches the end of the file; otherwise, the function will return FLI_EOF
. If no error occurs, this function will return FLI_OK
, but if an error has occurred, it will return FLI_ERROR
or FLI_NOT_OPEN
. One useful return value is FLI_EOF
, which tells you that the playback has reached the last frame of the file.
What about drawing each frame image? The frame is read into the global variables fli_bitmap
(which contains the current frame image) and fli_palette
(which contains the current frame's palette).
extern BITMAP *fli_bitmap; extern PALETTE fli_palette;
Even if you are running a program in a high-color or true-color video mode, you will need to set the current palette to render the animation frames properly. (This at least applies to 8-bit FLI files; FLC files might not need a palette.)
After each call to next_fli_frame
, Allegro sets a global variable indicating the current frame in the animation sequence of the FLI file, called fli_frame
.
extern int fli_frame;
The current frame is helpful to know, but it doesn't help with timing, which will differ from one FLI file to another. Allegro takes care of the problem by automatically incrementing a global variable called fli_timer
whenever a new frame should be displayed. This works regardless of the computer's speed because it is handled by an interrupt. It is important to pay attention to timing unless you are only concerned with the image of each frame and not playback speed.
extern volatile int fli_timer;
Each time you call next_fli_frame
, the fli_timer
variable is decremented, so if playback is in sync with timing, this variable will always be 0 unless a new frame is ready to be displayed. This makes it easy to determine when each frame should be drawn.
To demonstrate the low-level FLI animation routines, I've written a short program called LoadFlick. The output from this program is shown in Figure 19.2. LoadFlick pretty much demonstrates everything you need to know about the low-level FLI routines, including how to load an FLI file, keep track of each frame, manage timing, and blit the image to the screen.
#include <stdio.h> #include <allegro.h> #define WHITE makecol(255,255,255) int main(void) { int ret; //initialize Allegro allegro_init(); set_color_depth(16); set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); install_timer(); install_keyboard(); //load the fli movie file ret = open_fli("octahedron.fli"); if (ret != FLI_OK) { allegro_message("Error loading octahedron.fli"); allegro_exit(); return 1; } //display movie resolution textprintf_ex(screen, font, 0, 0, WHITE,0, "FLI resolution: %d x %d", fli_bitmap->w, fli_bitmap->h); //main loop while (!keypressed()) { //is it time for the next frame? if (fli_timer) { //open the next frame next_fli_frame(1); //adjust the palette set_palette(fli_palette); //copy the FLI frame to the screen blit(fli_bitmap, screen, 0, 0, 0, 30, fli_bitmap->w, fli_bitmap->h); //display current frame textprintf_ex(screen, font, 0, 10, WHITE,0, "Current frame: %4d", fli_frame); } } //remove fli from memory close_fli(); //time to leave allegro_exit(); return 0; } END_OF_MAIN()
Let's do something fun just to see how useful the low-level FLI routines can be when you want full control over each frame in the animation. The ResizeFlick program is similar to LoadFlick in that it opens an FLI into memory before playback. The difference in this new program is that the resulting FLI frames are resized to fill the screen (using a proper ratio for the height). Note that the FLI file must be in landscape orientation—wider than it is tall—or the bottom of each frame image might be cropped. It's best to use FLI files with a resolution that is similar to one of the common screen resolutions, such as 320 × 240, 640 × 480, and so on.
Figure 19.3 shows the ResizeFlick program running with a short animation of a jet aircraft (the U.S. Air Force SR-71 Blackbird). Note the black area at the bottom of the screen—this is due to the fact that the original FLI animation was 320 × 200, so when it was scaled there were pixels left blank on the bottom. If you want to truly fill the entire screen, you can do away with the width
and height
variables and simply pass SCREEN_W-1
and SCREEN_H-1
as the last two parameters of stretch_blit
, which will cause the FLI to be played back in true full-screen mode (although with image artifacts if the scaling is not a multiple of the original resolution).
#include "allegro.h" #define WHITE makecol(255,255,255) #define BLACK makecol(0,0,0) int main(void) { int ret,width,height; //initialize Allegro allegro_init(); install_timer(); install_keyboard(); //set video mode--color depth defaults to 8-bit set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); //load the fli movie file ret = open_fli("particles.fli"); if (ret != FLI_OK) { allegro_message("Error loading animation file"); allegro_exit(); return 1; } //main loop while (!keypressed()) { //is it time for the next frame? if (fli_timer) { //open the next frame next_fli_frame(1); //adjust the palette set_palette(fli_palette); //calculate scale width = SCREEN_W; height = fli_bitmap->h * (SCREEN_W / fli_bitmap->w); //draw scaled FLI (note: screen must be in 8-bit mode) stretch_blit(fli_bitmap, screen, 0, 0, fli_bitmap->w, fli_bitmap->h, 0, 0, width, height); //display movie resolution textprintf_ex(screen, font, 0, 0, BLACK, 0, "FLI resolution: %d x %d", fli_bitmap->w, fli_bitmap->h); //display current frame textprintf_ex(screen, font, 0, 10, BLACK, 0, "Current frame: %4d", fli_frame); } } //remove fli from memory close_fli(); allegro_exit(); return 0; } END_OF_MAIN()
This chapter provided an overview of the FLIC animation routines available with Allegro. You learned how to play an FLI/FLC file directly from disk as well as how to load an FLI/FLC file into memory and manipulate the animation frame by frame. There were three sample programs in this chapter to demonstrate the routines available for playback of an FLIC file, including a program at the end of the chapter that displayed a movie scaled to the entire screen.
You can find the answers to this chapter quiz in Appendix A, “Chapter Quiz Answers.”
3.138.36.38