The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Using Timers in Allegro 4

From Allegro Wiki
Revision as of 16:17, August 3, 2007 by CGamesPlay (talk | contribs) (recategorization)
Jump to: navigation, search

Introduction

Welcome to my tutorial on Allegro timer usage. You should read this if you have problems making your game run at the right speed on your computer, or if you made a game and put it on another computer just to find out that it suddenly runs at a different speed. This tutorial is supposed to teach you how to use Allegro timers to make your game run at the right speed on all computers. If this tutorial doesn't help, go to allegro.cc and search the forums, this subject has been discussed _alot_.

To make use of this tutorial: You should be familiar with Allegro, if not visit http://www.allegro.cc/ and http://alleg.sourceforge.net/. You should also have tried to make a game run at the right speed and felt that there must be a better way.

Timer

What?

Timers are a part of the allegro library, so include allegro.h and don't forget to link to the lib when you compile. <highlightSyntax language="cpp">#include <allegro.h></highlightSyntax>

Now I shall explain how a timer works. You use the function install_int or install_int_ex to start a timer. These functions take a function parameter void *(proc)(), and a speed parameter. The function you pass to the timer will be called as often as the speed parameter dictates. I call each call to the function a 'tick'. If you use install_int, speed is the time in milliseconds between ticks.

But timer itself doesn't use milliseconds, they are converted to hardware clock ticks. The other function is the one I always have used, you'll see why. The speed parameter of install_int_ex takes hardware ticks instead of milliseconds. Using this function as is would of course be trickier than the easy to use millisecond scale. But Allegro has a bunch of usefull macros defined that converts from a usefull scale to hardware ticks.

I have copied the description of all macros from the allegro docs.

     SECS_TO_TIMER(secs)  - give the number of seconds between
                            each tick
     MSEC_TO_TIMER(msec)  - give the number of milliseconds
                            between ticks
     BPS_TO_TIMER(bps)    - give the number of ticks each second
     BPM_TO_TIMER(bpm)    - give the number of ticks per minute

You can see that the first two take normal time values, seconds and milliseconds. Using the second would have exactly the same effect as using install_int. These would be useful for timing things, session length, reaction time and such. But if you want a light that flashes 10 times per minute or an object that moves 30 pixels per second you should use the other two.

How?

First, you need a function that the timer will call every tick, the ticker. This ticker function must execute very fast, so all we're going to do is to increase a tick counter. Since timers are using interrupts to call your ticker there are some special rules for the safety of your program. You should read the documentation for specific information about this.

<highlightSyntax language="cpp"> //One of these rules state that the variables handled in your ticker need be volatile

  //So let's declare it globally.
  volatile int ticks=0;

  //Then comes the ticker, in my example it simply increments ticks.
  //After the function another rule bothers us.
  //You need the macro END_OF_FUNCTION after the ticker.
  void ticker(){
     ticks++;
  }END_OF_FUNCTION(ticker)

  //Time to install our timer.
  //Before doing that, we simply must follow the rules.
  //Lock variable and function using those nice macros.
  void main(){
     LOCK_VARIABLE(my_time);
     LOCK_FUNCTION(my_timer);

     //Finally we can install the timer, using either method.
     install_int(my_timer,10);
     install_int_ex(my_timer,BPS_TO_TIMER(100));
  }END_OF_MAIN()

</highlightSyntax>

And now we have a timer that ticks 100 times per second.

Making use of the ticks

Before the timer your main game loop would in general look like this.

<highlightSyntax language="cpp"> while(game_on){

     update_stuff();
     draw_stuff();
     delay();
  }

</highlightSyntax>

Without the delay, it might be a pause, for loop or even vsync, your program runs as fast as it can. I guess your best result when trying to run your game at the same speed on any computer would be when using vsync. But vsync isn't very reliable, it works different on different systems and causes all kinds of problems.

Now we use the ticks instead. Let's look at the whole picture.

<highlightSyntax language="cpp"> #include <allegro.h>

  volatile int ticks=0;
  void ticker(){
     ticks++;
  }END_OF_FUNCTION(ticker)

  void main(){
     setup_stuff();

     LOCK_VARIABLE(ticks);
     LOCK_FUNCTION(ticker);
     install_int_ex(ticker,BPS_TO_TIMER(60));

     int ntick;
     while(game_on){
        ntick=ticks;
        ticks=0;
        for(;ntick>0;ntick--){
           update_stuff();
        }
        draw_stuff();
     }
  }END_OF_MAIN()

</highlightSyntax>

As you can see I create a timer, and install it at 60 BPS. Then I start the game loop. In this I update the stuff as many times as it's supposed to be updated the current loop. Each time the timer ticks, the logic is supposed to be updated. I simply update as many times as there has been ticks since the last loop. I use the mehod shown to take care of any eventual situations. If the drawing takes more than one tick, it updates for every tick before drawing again. If there has been no ticks since last time just draw again. If the updating is especially slow I only update as many ticks as I have since the last loop. This ensures that the player wont have to wait to long for a visual confirmation that the game hasn't hung itself.

Yielding the CPU

You many notice in the previous example that your program uses a lot of CPU. Even while just waiting for ticks to increment above 0 your cpu is busy continously executing those simple loop and check instructions. Besides taking time away from other programs, this method uses a lot of power and can cause fans to turn on whereas they would normally be off. There is no need to worry as there is a perfeclty simple solution!

Our goal is to put the process to sleep while we are busy waiting and to wake it back up when we are ready again. The trick is to use a semaphore. Below is some code which will run natively on linux and mac os x. Windows has its own semaphore mechanism but the pthreads-win32 [1] library provides the means to run this code under windows.

<highlightSyntax language="cpp"> #include <allegro.h>

  1. include <semaphore.h>
  sem_t ticks;
  void ticker(){
     sem_post(&ticks);
  }END_OF_FUNCTION(ticker)

  void main(){
     setup_stuff();

     sem_init(&ticks, 0, 1);
     LOCK_FUNCTION(ticker);
     install_int_ex(ticker,BPS_TO_TIMER(60));

     while(game_on){
        sem_wait(&ticks);

update_stuff();

        draw_stuff();
     }
  }END_OF_MAIN()

</highlightSyntax>

Notice we replaced instances of tick increment and decrement with function calls. If you wish to check the count of the ticks you should use the function sem_getvalue [2]. The complete documentation is available at opengroup [3].

FPS

Sometimes it's good to know how many frames your game produces. The standard value to measure this is FPS, frames per second. Using the method I've described, logic updates with the same interval on all computers, but the drawing does not. And that's what's interesting, you can check the fps on your game on different computers to decide required and recommended systems. Some people also like to see their fps so they can brag about their computers.

<highlightSyntax language="cpp"> void main()

  {
      while(gameon)
      {
      int Frames = 0; // amount of frames drawn
      int fps = 0; // amount of frames drawn in a single second
      int DidTicks = 0; // How many frames we have left in this second
      ntick=ticks;
        ticks=0;
        for(;ntick>0;ntick--){

           update_stuff();
           DidTicks++;
           if(DidTicks == 60)
           {
               fps = Frames; 
               DidTicks = 0;
               Frames = 0;
           }
        }
        Draw();
        Frames++;
      }
}

</highlightSyntax>

Simple enough, we know that once the logic has run 60 times a second has gone by. When that happens, we simply copy our count of frames to the fps variable. However, this has some problems. Its only updated every second. What if we want to update it every frame? Well, the next example will show how to do that.

<highlightSyntax language="cpp"> void main(){

     int frames[60];	//Store the frames produced for every tick.
     //Reset the array.
     for(int i=0;i<60;i++)
        frames[i]=0;
     int framecount=0;	//Counts the frames during the current tick.
     int fps=0;	//The current fps value.

     while(game_on){
        ntick=ticks;
        ticks=0;
        for(;i>0;i--){
           fps-=frames[59];		//Remove the oldest frames.
           fps+=framecount;		//Add the new frames
           //Push back all frames to make room for the new.
           for(int i=59;i>0;i--)
              frames[i]=frames[i-1];
           frames[0]=framecount;	//Add the new frames to the array.
           framecount=0;
           update_stuff();
        }
        draw_stuff();
        framecount++;			//Increment counter.
     }
  }END_OF_MAIN()

</highlightSyntax>

I've removed all the irrelevant code to save space, don't do that in your program. The method is simple.I store the number of frames for each tick the last second. When the logic is updated I push out the oldest frames and add the new frames. But before I update the array, I update the fps by doing the same thing. The new frames are taken from the framecount variable that is incremented each frame, and it is reset every logic update.