By Pete
[email protected]
For real time games, you will want to specify the frame rate at which it runs. Unfortunately, there is a huge range of PC hardware and software combinations out there, so you can't assume that everyone will experience your creation at the same speed as it is on your machine. Fortunately, Allegro makes it very easy to do this correctly. It's in the FAQ, but it is surprising how many games don't bother to implement it.
The code here uses a simple bouncing circle to illustrate
the point. Such simple animation should run OK on all but the oldest PCs. I
have includes a pretend delay to simulate the effect of doing lots of
processing, which will slow down your maximum frame rate. In real life,
this would be moving objects, collision detection, AI, and so on. Another chunk
of processing time is taken up by composing the screen; drawing backgrounds,
objects and characters. It's sometimes useful to split these two activities up,
as I will demonstrate later.
The code is written in C, it will compile under DJGPP/DOS using the latest
Allegro. I've also checked it under MinGW/WindowsMe. Feel free to use and
modify it, subject to the usual Allegro "std_disclaimer.h"
It should also work on other platforms, let me know if not.
This is just the skeleton, with no control. On my system, it achieves 200 fps - yours might be faster or slower. Use the +/- keys on the keypad to introduce a pretend processing delay. You'll see that the frame rate is very dependent on that, as explained in the following diagram. Time is on the horizontal axis.
(Slow
computer)
(Fast computer)

For the fast computer, processing the logic and drawing the graphics just takes less time, so the frame rate goes up.
Suppose we want to cut our frame rate right down to 25 fps. Assuming the processing takes no time at all, we just introduce a delay of 1/25 sec, i.e.. 40 ms. we can then allow for the processing time by tweaking the 40 down a bit, until it looks about right. If the processing per frame really is very short compared to the frame rate, this method is OK. In general though, there are three problems:
And there's a further difficulty - as you develop the game, the amount of processing may change. You add more features, or do things more efficiently, or switch between debug (no optimization) and release (optimized) versions. You'll need to keep altering that delay.
This is a not recommended, but the VGA does have a vertical
interrupt every 50-70ish frames per second, and you can synchronize on this.
Just call
vsync();
every frame. I think Allegro simulates the vsync for modes that don't have it.
Anyway, it's very crude, as you have to stick to one rate. I would say, don't
so this.
This is a much better way. You use Allegro's timer facility
to set an event every 40 ms. you then do your frame and wait for the event to
occur. In the following diagram, the ticker is shown as the black line on the
top. If a tick has not occurred, the program will simply wait. So, as long as
the total processing does not take more than 40ms, the program will have the correct frame rate regardless of
what computer it runs on.
(Slow
computer)
(Fast computer)

In more detail, you need a variable to signal that the event
has occurred, anta function to be called, by the system, which updates it.
volatile int
ticks;
void ticker(void)
{
++ticks;
} END_OF_FUNCTION(ticker);
The volatile tells
the compiler that this variable might change at any time in your main program.
The END_OF_FUNCTION
is a bit of mechanics that lets us lock the function in memory
(see the FAQ/Allegro manual for why this is needed).
Next, you need to do that locking, only once, at the start of your program
LOCK_FUNCTION(ticker);
LOCK_VARIABLE(ticks);
Now, start a timer running at 25 fps.
install_int_ex(ticker,
BPS_TO_TIMER(25));
You can also do this once, at the start of the program. It doesn't do much harm
to leave it running, but make sure you don't keep calling it, or you can run in
to problems. The number of timers is limited, so to be super correct, use
install_int_ex just before you need it and use remove_int when finished.
The final bit goes in the main loop; it just waits for the event to occur
while
(ticks<1)
{
yield_timeslice();
}
ticks=0;
In other words, wait in an endless loop for ticker to increment ticks. Then
reset ticks. The loop continues, and time elapses, until it gets back here.
If you try example 3, you will see that you can alter the pretend delay quite a
lot, and the program will stay steady on 25 fps. If you exceed 40 ms (or so) on
the delay, the rate will drop. This corresponds to the PC going flat out;
there’s too much processing for it to achieve this frame rate.
As a small refinement, you will often find that actually composing an image and blitting it to the screen is the slowest part of the processing. If you don't do this, you can 'catch up' on frames, at the expense of making it jerky. In the following diagram, the graphics time has gone up so that a complete cycle of logic and graphics takes too long. If the tick has already occurred when the program starts to draw the graphics, it skips this stage and does two logic blocks in a row.


Example 4 splits the delay in two, with a fixed portion (25
ms) for the graphics. The extra line
if (ticks==0)
{
/* draw graphics */
...
}
only does the graphics if we haven't already exceeded our 40 ms. Try the
program; you'll see it going jerky as you pass 40 ms. In the limit, there's
only enough time to do the logic, and the graphics never get updated.
You can go to town on this, for example drawing more elaborate explosions if
the PC is fast enough. Only bother if you've really got nothing better to do!
Another special case is when you can draw frame N without needing to go through previous frames. You can't do it in a game, because what happens must depend on what the player's been doing up to that point. But for a title page, you might have some rotating things, scrolling text, something of that nature. In that case, you can use a ticker as before, but run your loop at full speed, potentially redrawing the same image many times. In the example, you'll see a rotating cross. The image for frame N is the cross, rotated by N "Allegro degrees" - in other words, it rotates a full circle every 256 frames. I could draw frame 99, say, without drawing any others. As you increase the delay, the cross rotates at the same speed, but it is jerkier. For example, if the real frame rate is only 5 fps, it only draws every fifth frame = (25/5). The result: fast and slow PCs see the same thing, but the faster one sees smoother graphics.
The purpose of this demo is to
show that you can do frame rates properly without much effort. The result will
be that your game can be enjoyed by more people because it always runs
at the right speed. Dell is selling a server at the moment with multiple Intel
Itanium processors in it. At the moment it costs 56000 US dollars, but I can
predict it won't be long before we all have one. And, it will run
Allegro! So plan for the future now.
Cheers,
Pete