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

Tutorial 3: Timers

From Allegro Wiki
Jump to: navigation, search
Outdated: Doesn't match current api (edit)

Abstract

Note: After [an enlightening discussion], I have decided against recommending this method of timing. If you would like to see a tutorial on how I would currently suggest it, please nag me by sending me an email. Thank you!

In this tutorial, we will learn how to use Allegro 5's new timer interface. You will find that it is much more convenient to use than the suggested methods for Allegro 4 and previous versions.

Data Files

   * hello2.c: Source code
   * times.ttf: A font needed for this tutorial

Tutorial

This example builds on the previous one: TrueType Fonts and Display Modes. Please read it first, or you will probably have no idea what is going on.

Timers are critical for all real-time games. Most people jump in without thinking about this topic, and get a little lost and confused when their awesome game runs at a different speed on their friends' computers. After reading through this brief tutorial, you will not have this problem. In fact, you'll learn that using timers is actually pretty easy, and helps keep your code structured.

As I suggested for the previous tutorial, make sure to download the code and have it by your side as you read this document. Is it on the screen now? Good.

Skimming through the source code, the first new thing you'll notice in the main function is that we are creating an extra color: black.

<highlightSyntax language="c"> ALLEGRO_COLOR black = al_map_rgba_f(0.0, 0.0, 0.0, 1.0); </highlightSyntax>

This will be referenced later on when we are clearing the screen. Note that the alpha value is still 1. That ensures that this is a solid black, and not translucent. (Recall from the previous tutorial that this is not the only way to create this color!)

Next we jump straight into creating and starting the timer.

<highlightSyntax language="c"> int refresh_rate = al_get_display_refresh_rate(); if(refresh_rate == 0) refresh_rate = 60;

ALLEGRO_TIMER *timer = al_install_timer(1.0/refresh_rate); al_start_timer(timer); </highlightSyntax>

The first thing we do is check the refresh rate of the monitor. This way we can update our game at the screen's update speed. Unfortunately, not all computers are capable of providing this information. In that case, al_get_display_refresh_rate will return zero. If this happens, we assume it is 60. 60 Hz is a very common refresh rate for monitors, though it is a little archaic now. (On a CRT, I can see the screen updating at this slow pace, and always tell my operating system to run at 85 Hz. Also, my laptop appears to update at 50 Hz. This is acceptable on an LCD because it does not flicker. In any case, 60 Hz is what Windows will default to using most of the time, until the user fixes it.) This is a good value to assume if it cannot be determined.

Next we install the timer. The value passed to al_install_timer is the speed at which this timer should "tick", in seconds. The equation I have set up will make it tick refresh_rate times each second, or that a single tick takes 1/refresh_rate seconds. This way we only process the game logic as often as we can see it. Immediately after installing the timer, we make it start ticking.

The next addition you will notice is that instead of just adding the keyboard to the event queue, we also add the timer.

<highlightSyntax language="c"> al_register_event_source(queue, (ALLEGRO_EVENT_SOURCE*)al_get_keyboard_event_source()); al_register_event_source(queue, (ALLEGRO_EVENT_SOURCE*)timer); </highlightSyntax>

You can add as many event sources to an event queue as you like. This way when we probe the queue for an event, it can return both keyboard and timer events.

In the main loop, we check what type of event comes in and acting accordingly.

<highlightSyntax language="c"> case ALLEGRO_EVENT_KEY_DOWN: { if(event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) return 0; break; } </highlightSyntax>

If an ALLEGRO_EVENT_KEY_DOWN event was just fired, then we enter this block. If the key which was pressed down was escape, then we return 0, otherwise we just break out of the switch and continue waiting for events. This is the same behavior as the previous example, it is just presented a little differently.

If an ALLEGRO_EVENT_TIMER event was processed, we do a little more work. Ideally, the code in this block would call some extra functions so that your event processing loop is cleaner.

<highlightSyntax language="c"> time = al_current_time(); dt = time-old_time; </highlightSyntax>

First we set the time variable to be the current number of seconds since the program started. Did you catch that? The function al_current_time returns a floating-point representation of the number of seconds elapsed since the program started execution. Again, this is a float, not an int, so only rarely will it be a whole number. Then the change in time between frames is computed by subtracting the old time from this value. On the first frame, it will likely be either very small or very large, depending on how long the program took to initialize, as well as other factors.

<highlightSyntax language="c"> if(dt > 0.25) dt = 0.25; </highlightSyntax>

Also, sometimes a user's computer will lag a little bit. Since dt is how we will base coordinate updates of everything in the game, it would be unfair to penalize the player for this. If some sort of lag happens where it takes more than a quarter of a second between frames, cap that time at exactly half a second. However, if you are making a multiplayer game where each player is on a different computer, this will not work, as the games must remain synchronized. In this case, simply remove this check.

Next we update the position of the text.

<highlightSyntax language="c"> text_x += 10 * dt; </highlightSyntax>

If dt were 1.0, then text_x would update by 10. In reality, dt will be much smaller. What this does is it requires a full second to pass before text_x is incremented by 10. Thus, we know that the text will be moving at 10 pixels per second.

Now we want to start manipulating the screen. Drawing is usually the most time-consuming part of a game, and care must be taken to ensure that we do not cause an event traffic jam.

<highlightSyntax language="c"> if(time - event.timer.timestamp <= 1.0/refresh_rate) { </highlightSyntax>

Each event is timestamped with the number of seconds since the program began. This conditional is checking that the tick event happened within the past frame. If the timer tick is from more than a frame ago, we disregard it and will not perform any drawing operatings.

One thing we did not do in the first example was clear the display. Allegro 5 will automatically blank the display when you initialize it, and since we were only drawing to it once, we had no reason to clear it again. This time we will be drawing to the display many times, so before each frame we must first clear it.

<highlightSyntax language="c"> al_clear_to_color(black); </highlightSyntax>

This will clear the screen to the black color we specified earlier. If you were to change the alpha value, you might expect the screen to only partially be cleared. This will not happen. Instead, it replaces every pixel in the display with the color specified. Thus every pixel will start out with an alpha value of 1.

Finally, we are done with the frame.

<highlightSyntax language="c"> old_time = time; </highlightSyntax>

We set the old_time variable equal to time so that the next frame can compute the change in time between frames, and repeat the whole process over again.

To compile this example, you will need to link with the Allegro 5 library as well as the allegro_ttf library. See the Tutorial 2: TrueType Fonts and Display Modes for more detailed compilation instructions. Bold text