The SteelGolem Ruins
Tutorials



Allegro Tutorial 2:
Game Timing with Allegro

Game Timing Overview

Consider the following code:

// in some function...

    while (!key[KEY_ESC])
    {
        if (key[KEY_LEFT]) player.x -= player.speed;
        if (key[KEY_RIGHT]) player.x += player.speed;

        clear (screenbuf);
        draw_sprite (screenbuf, player.sprite, player.x, SCREEN_H/2);
        blit (screenbuf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    }

If the left or right arrow keys are being pressed then the player's position is changed. After we check the keyboard, we draw the sprite on the screen using the player's position. We keep doing that until the escape key is pressed.

What's important to note here is that the player's position changes at a constant rate. If you tested this code on two computers with a decent speed difference (ie: p1-133mHz vs p3-700mHz) you'll see that the player sprite will move at noticibly different speeds. The speed that the loop is run at is determined by how fast the computer runs, and since the two computers run at considerably different speeds, the player will move at different speeds on each computer. Suppose you used only one computer and ran the game in 8bpp color mode and then again in 32bpp color mode, you'll probably notice a difference in the speed the player moved. The point I want to get across is that you most likely want your game to look like it runs at the same speed no matter what. We're going to need to make some changes for that.

Game Timing - The Old Way

"The Old Way", as I like to call it, sets up a timer and races the logic against the timer. Here is how you would alter the above code to use it:

// globals

volatile int speed_counter = 0;
void increment_speed_counter()
{
    speed_counter++;
}
END_OF_FUNCTION (speed_counter);

// later, in some function...

    LOCK_VARIABLE (speed_counter);
    LOCK_FUNCTION (increment_speed_counter);
    install_int_ex (increment_speed_counter, BPS_TO_TIMER(60));

    while (!key[KEY_ESC])
    {
        while (speed_counter > 0)
        {
            if (key[KEY_LEFT]) player.x -= player.speed;
            if (key[KEY_RIGHT]) player.x += player.speed;
            speed_counter--;
        }

        clear (screenbuf);
        draw_sprite (screenbuf, player.sprite, player.x, SCREEN_H/2);
        blit (screenbuf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    }

First thing we need to do is tell the compiler about a variable and function we're going to use for the timer. Going over the timer section in the Allegro docs will tell you all about what's being done. Sometime before we get to the main loop we need to lock that variable and function, and then install a timer using the function.

To explain things, increment_speed_counter() is increasing speed_counter by 1 every 1/60th of a second - on every computer. I'm not sure where the 60 comes from, but Inphernic in #allegro on efnet had this to say: "<@Inphernic> i'd guess it has something to do with the lowest refresh rate being 60hz". Anyways, while speed_counter is increasing by 1 every 1/60th of a second, the logic loop is trying to get it to 0 by decreasing speed_counter by 1 as fast as it can. When it hits 0, we get to draw a frame.

Game Timing - My Way

This is how I do it now:

// globals

#define TIME_ELAPSED_MIN 10
volatile unsigned long time_now = 0;
void time_now_handler ()
{
    time_now += TIME_ELAPSED_MIN;
}
END_OF_FUNCTION (time_now_handler);

unsigned long time_last = 0;
double time_elapsed = 0.0;

// later, in some function...

    LOCK_VARIABLE (time_now);
    LOCK_FUNCTION (time_now_handler);
    install_int (time_now_handler, TIME_ELAPSED_MIN);

    while (!key[KEY_ESC])
    {
        time_elapsed = (time_now - time_last) / 1000.0;
        time_last = time_now;
        if (key[KEY_LEFT]) player.x -= player.speed * time_elapsed;
        if (key[KEY_RIGHT]) player.x += player.speed * time_elapsed;

        clear (screenbuf);
        draw_sprite (screenbuf, player.sprite, player.x, SCREEN_H/2);
        blit (screenbuf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    }

There is a bit more code here, but I feel it helps in understanding what is going on. First we need a variable and function for the timer. The variable stores a value in msecs, and the function increases that value by a constant, TIME_ELAPSED_MIN - the minimum amount of time we can add in. We set it to 10 because its a 'safe' number to use - anything lower and we can't guarantee it will keep up. Next we declare two more variables, one to store the time for the last time we checked (in msecs), and the other to store the amount of time that went by between checks. I set it up to be measured in seconds because I can imagine something moving at n pixels per seconds easier than n pixels per millisecond. These two values can be declared either in the function that uses them for the logic, or globally as I show above. Next we lock the timer variable and function and install the timer. The timer is going to be updated every TIME_ELAPSED_MIN msecs, which is exactly what we want.

Before we do any game logic, we should first update the time_elapsed variable and the time_last variable. Then we use the time_elapsed by multiplying it by a value we measure in game-units per second. Here the game-unit will translate directly to pixels. Other game-units may be measured in 3D terms, which won't translate directly to pixels at all.

Pros and Cons of the Game Timing Methods

The Old Way

My immediate problem with this solution is that I have no idea how to tell how fast the player will move. Another problem is that if the computer is too slow, it won't be able to decrease speed_counter fast enough to beat the timer that is increasing it. This means that we will never get out of the while (speed_counter > 0) loop and never update the screen. There are ways to get around this, but I'll leave that up to you to look into. The final problem I'm going to mention is that I don't really understand how this works. This problem ties in with the first one I mentioned, because I can't figure out how fast the player will move if I don't understand how it works on a fundamental level. If you can shed light on the subject, please let me know; I'd really like to understand.

Though I have quite a few complaints, there are good reasons for using this method. The best reason I see to do it this way is that although the screen is being updated at a non-constant rate, the game logic isn't. Our player moves by the same amount every single logic pass. What this means is that when we check for collisions, if we set the player move speed to something less than or equal to the size of our tiles (in the case of a tile map) then the player won't ever be able to jump through blocks. That makes collision checking a whole lot easier.

My Way

One problem with this method is that if we get back to the time_elapsed calculation before time_now gets updated, then time_elapsed will equal 0 (even though time has obviously passed). We need to either wait until (time_now - time_last) > 0 or let the program deal with that case if it needs to. Another problem is caused because we have to limit the minimal update speed of our timer; this forces a speed cap on the program. My final complaint is that because the game can do different amounts of work during the logic pass, the time_elapsed calculation can be anywhere from 10 milliseconds to something relatively big. Since our movement calculation depends on the time_elapsed calculation, our sprite can move in small or big jumps - and we have no control over how big the jumps will be. This means it could be possible for our player to jump right through blocks if we're not careful.

For me, this method is the choice to go for because it makes sense. I don't like trying to implement things that don't make sense to me, whether I'm told it will help speed my games up or not. If I don't understand, I'm not likely going to use it. However, different needs merit different choices - I've used The Old Way from time to time, and I still make use of it now. I understand most of it, and I've seen good benefits. Make your choice with care: You might not like what you get if you don't consider your options.

Any comments or improvements on either method are welcome, let me know what you think.

Tracking the Game Speed

In order to get an idea of how fast your program is running, you can implement a Frames Per Second (FPS) tracking system. This is really simple to do:

// globals

volatile int frame_count = 0, fps = 0;
void fps_timer_handler ()
{
    fps = frame_count;
    frame_count = 0;
}
END_OF_FUNCTION (fps_timer_handler);

// later, in some function...

    LOCK_VARIABLE (frame_count);
    LOCK_VARIABLE (fps);
    LOCK_FUNCTION (fps_timer_handler);
    install_int (fps_timer_handler, 1000);

    while (!key[KEY_ESC])
    {
        if (key[KEY_LEFT]) player.x -= player.speed;
        if (key[KEY_RIGHT]) player.x += player.speed;

        clear (screenbuf);
        draw_sprite (screenbuf, player.sprite, player.x, SCREEN_H/2);
        textprintf (screenbuf, font, 0, 0, makecol(255,255,255), "fps: %d", fps);
        blit (screenbuf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

        frame_count++;
    }

First you get the timer variable and function going, then lock them and setup the timer. We want the fps_timer_handler to fire every 1000msecs (1sec) and during that time we'll update the frame_count every time we draw. When the fps_timer_handler fires - after 1 second - it stores the frame_count into the fps and resets it to zero. We then use the fps however we need to; here I just print it out in white text. Note that the FPS tracker can be used with either Game Timing method I've shown above. Enjoy!