What is a sprite?
I don't know if there's an official definition, but I'll call a sprite anything that changes its look periodically. They are often found in games as characters, terrain tiles, etc...
Why the hell should I use a sprite?
Because you don't want the player to play a floating statue. Do you?
What's the ideal structure for a sprite?
The really important thing is that you have several images for each position; as you increase the number of images, the animation gets smoother. But do not overdose your game and your artist with thousands of images per position! As you will see in the demo, even this 2-framed character seems like he's really moving. Now watch games like Rockman X5 and you'll see what is a real smooth animation.
Creating your function
Locking
Installing
Your interrupt is now running, sir! Don't forget to remove it after it is useless:
anim: It is a counter for how many animation ticks have gone, and when it reaches a defined number, it changes the animation. The bigger the number, the slower the animation. It is not just a flag because it needs to balance the rating between movement and animation, and also you may want some frames to last longer.
move: Not exactly meant to the animation, but to keep the scrolling movement of the character fixed, no matter how many frames per second your program is drawing.
Actually, if you want a flexible and dynamic sprite engine, you'll have to do your own sprite struct, or preferably, a class. That's NOT what we'll do in this article. We will make a very simple sprite engine(though it cannot be called an engine) for a very simple program, so that you can understand its idea and make one that matches what you'll need for your game.
Our program
The first thing to know is what kind of game are we gonna do? A Final Fantasy 3 clone? A fighting game? Obviously, sprites are not as useful for 3D games as they are to 2D ones, since you keep each image stored with sprites, and renders your images while running the game on vector-based graphics. Back to the topic, we will do a very stupid program. One guy is on the screen. You can move him left or right. Pretty complicated, uh? To keep it simple, all animations will have only two frames(different images on the animation). The main character will be my magnum opus drawing: MatchET. He's perfect because he's easy to draw :) On the other hand, his animation sucks. Why, you ask. Simple, just look at him.



Initializing stuff
Here's the code for the main function:
int main(void){
allegro_init();
install_keyboard();
install_timer();
set_color_depth(16);
set_gfx_mode(GFX_AUTODETECT, 320, 240, 0, 0);
tha_thing();
allegro_exit();
return 0;
}END_OF_MAIN();
Just the common Allegro code. Note the install_timer() function, which is the key for animating. The unaware may say 'Whaddahell man! I'm not gonna put a clock or anything, dude! Take that timer thing away...'. But that's exactly the most important thing about it. If you let the animation function free in the main loop, it will run totally different on different computers. Let's say it, if you build your game in my computer, which is damn slow, and then you run it on a fast one, it will be unplayable... Your character will be moving as fast as Santa Claus in Christmas night, because the computer will do stuff faster than you previously measured. How to fix it: you have to make it linked to a timed component. When using Allegro, the common step is the one we will see: Interrupts. Yes they are low level, boring and damn unclear. But they are the easier way to track the animation.Using interrupts
It is simple: you create your function, locks its variables, locks itself, and installs it, making it runs at as many times a second you want(and your hardware supports). Let's do it by parts.
It's a common function to be called from the main loop:
volatile int anim, move;
void run(void){
anim++;
move++;
}END_OF_FUNCTION(run);
But there are some restrictions:
First, you have to declare your variables as 'volatile', so they can be called by the program 'anywhere anyhow!'. Obviously they must as well be global variables.
Second, and very important, is that it may not be a complicated algorithm, since it will run many times a seconds, and you don't want to fry your O.S. down. Even you if don't understand why, take my advice, do NOT create long stuff inside it.
Third, you must append the macro END_OF_FUNCTION(run) at the end of the function. It is for Allegro internal tasks.
Yes, it is necessary. Why? Just because if you're a bad luck one like me, your program will try to access the variables at the same time the interrupt is. And it sounds like 'crash!'. And it does not take very much time to fix:
LOCK_VARIABLE(anim);
LOCK_VARIABLE(move);
LOCK_FUNCTION(run);
Got hurt?
That's the fun part.
install_int(run, 100);
install_int receives two parameters, the first is a function pointer to the referred function, and the second is the number of milliseconds between the ticks that call the function.
remove_int(run);
Purpose of the variables
Two variables are inside run(), but yet they seem useless. Here is their why:Code
It may not seem, but it's just this. Nothing else. Here's the final code for sawa.c. Notice that sawa.h is a file written by the Allegro Grabber.
#include "allegro.h"
#include "sawa.h"
#define TIME 1
#define STILL 0
#define LEFT 1
#define RIGHT 2
volatile int anim, move;
void run(void){
anim++;
move++;
}END_OF_FUNCTION(run);
void tha_thing(){
BITMAP *buffer;
DATAFILE *datafile;
short x, action, image;
x = 160;
action = STILL;
image = Still1;
buffer = create_bitmap(320, 240);
datafile = load_datafile("sawa.dat");
LOCK_VARIABLE(anim);
LOCK_VARIABLE(move);
LOCK_FUNCTION(run);
install_int(run, 100);
while(!key[KEY_ESC]){
clear(buffer);
if(anim >= TIME){
if(action == STILL){
if (image == Still1) image = Still2;
else image = Still1;
}
else{
if(action == LEFT){
if (image == Left1) image = Left2;
else image = Left1;
}
else{
if (image == Right1) image = Right2;
else image = Right1;
}
}
anim = 0;
}
if(key[KEY_LEFT]){
if(action != LEFT){
action = LEFT;
image = Left1;
anim = 0;
}
}
else{
if(key[KEY_RIGHT]){
if(action != RIGHT){
action = RIGHT;
image = Right2;
anim = 0;
}
}
else{
if(action != STILL){
action = STILL;
image = Still1;
anim = 0;
}
}
}
if(move > 0){
if(action == RIGHT){
x++;
if(x > 320) x = 320;
}
if(action == LEFT){
x--;
if(x < 0) x = 0;
}
move = 0;
}
draw_sprite(buffer, datafile[image].dat, x, 120);
blit(buffer, screen, 0, 0, 0, 0, 320, 240);
}
remove_int(run);
}
int main(void){
allegro_init();
install_keyboard();
install_timer();
set_color_depth(16);
set_gfx_mode(GFX_AUTODETECT, 320, 240, 0, 0);
tha_thing();
allegro_exit();
return 0;
}END_OF_MAIN();
Conclusion
That's all. I hope you could understand the main idea of using allegro timers to handle the animation counter(as well as motion, etc...). With a little effort you can have the same basics to your 90-terabytes-memory-of-pure-complex-code-game. Keep in mind that a good drawing won't do any well if the animation isn't handled correctly. Any question or flaming, just contact me(adresses in "About Me" page).
Introduction -
Our program -
Initializing stuff -
Using Interrupts -
Purpose of the variables -
Code -
Conclusion