Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Is there anything wrong with this type of game loop?

This thread is locked; no one can reply to it. rss feed Print
Is there anything wrong with this type of game loop?
Gnamra
Member #12,503
January 2011
avatar

My game loop has always been one timer for logic, and one timer for drawing.
But I just read this:

SiegeLord said:

adamk kromm said:


The way I understand it is you set up a bunch of timers. One for rendering, one for updating the logic, and one/some for getting input.

This seems insane, I don't know if anybody really does it like that. The event driven loops generally seen for A5 involve a single timer and are fundamentally the same to the game loop you posted. Once you add interpolation, they become practically identical:

Thread: https://www.allegro.cc/forums/thread/612179

The thread is locked so I couldn't reply there, but why is using one timer for logic and one for drawing "insane"? Am I misunderstanding SiegeLord?

Kris Asick
Member #1,424
July 2001

The thing is you really only need a single timer to track total elapsed time, then run your game logic and rendering independetly off of that in a single loop. What I basically do is:

do {
    // 1. Update timer and calculate how many logic frames to process
    // 2. Handle All Allegro Events in Queue
    while (logicframes > 0)
    {
        // 3a. Process Keyboard/Mouse/Joystick I/O
        // 3b. Process x Frames of Game Logic (Based on time passed)
        logicframes--;
    }
    // 4. Interpolate Object Positions for Rendering
    // 5. VSync and Render Frame
} while (!done);

--- Kris Asick (Gemini)
--- http://www.pixelships.com

SiegeLord
Member #7,827
October 2006
avatar

I'd say it's insane because the drawing timer seems completely unnecessary. There are two things that are of interest in the game loop as far as the drawing goes. First, you don't want to draw too often: if you have interpolation, drawing more often than the refresh rate of the monitor is wasteful; if you don't have interpolation, then drawing multiple identical frames is wasteful (i.e. drawing two frames in a row without a timer event between them). Second, you want to somewhat align your drawing rate to the refresh rate (to prevent tearing and it's useful for interpolation). Both are admirably well served by the combination of a logic timer and some vsync control.

The two reasons I can think of having a separate timer for drawing, are 1) you want to draw consistently slower than refresh rate; 2) you are trying to dogmatically use events for everything.

I really don't see the reasoning behind #1. If the idea is to provide a better user experience by keeping the FPS consistent, then I think interpolation will provide a better solution than artificially limiting the FPS. If interpolation is not an option, I'd explicitly drop frames instead of using a separate timer.

#2 is where the insanity starts to creep in. The reality of providing graphical output in a videogame is that there is an external hardware "timer" (the temporal sequence of monitor refreshes) that governs when you should draw. The way you "listen" to this event is by using a vsync'ing mechanism. To ignore that reality and to create an entirely unrelated source of events (a drawing timer) is nonsensical.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

It's not insane. There are good reasons to limit drawing to less than refresh rate, such as conserving batteries on portables and keeping laptops from heating up or desktop fans from spinning madly. That's why I use two timers with configurable draw rate.

SiegeLord
Member #7,827
October 2006
avatar

Ok. I'd still do it by explicitly dropping frames instead of adding the complexity of an additional timer.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

pkrcel
Member #14,001
February 2012

Uh, wouldn't an handheld device consume more batteries for keeping the screen lit, instead of computing drawings, I mean in at least one order of magnitude difference.

I think drawing at a slower pace while the screen is still lit would consume roughly the same amount of energy as when actively refreshing the screen....if not for the calculations on the GPU and so on...

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Trent Gamblin
Member #261
April 2000
avatar

No, it definitely makes a big difference...

pkrcel
Member #14,001
February 2012

Would this be the Money Drop I'd be losing my million against it no doubt, back in the days the main concern of my fellow customers was mostly to reduce power consumption modulating the screnn backlight usage ;D

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

axilmar
Member #1,204
April 2001

So what's actually the best way to implement a game loop? I've read the previous thread, I've read this thread, I've read various A5 tutorials. Which is the best way?

Trent Gamblin
Member #261
April 2000
avatar

I don't think there's 1 best way. There are a few variations that are pretty much the same. I think as long as you use al_wait_for_event and not poll the event queue primarily, it's good.

SiegeLord
Member #7,827
October 2006
avatar

axilmar said:

Which is the best way?

You need to state what you need out of it first. Depending on what you need, you will get different loops. The only critical A5 specific bit is that you need to be careful to drain the event queue and not pick off one event per frame. Otherwise anything goes, pretty much. I, personally, kind of like the latest game loop I came up with (it's in the thread linked to in the OP... reproduced below):

#SelectExpand
1const int MAX_FRAMESKIP = 5; 2const float FPS = 25; 3const float dt = 1.0 / FPS; 4 5ALLEGRO_TIMER* t = al_create_timer(dt); 6al_register_event_source(q, al_get_timer_event_source(t)); 7al_start_timer(t); 8 9float game_time = 0; 10float offset = al_get_time(); 11 12bool game_is_running = true; 13 14while(game_is_running) 15{ 16 int loops = 0; 17 ALLEGRO_EVENT e; 18 19 while(al_get_next_event(q, &e)) 20 { 21 if(e.type == ALLEGRO_EVENT_TIMER) 22 { 23 game_time = e.timer.count * dt; 24 update_game(); 25 loops++; 26 27 if(loops >= MAX_FRAMESKIP) 28 break; 29 } 30 } 31 32 float interpolation = (al_get_time() - offset - game_time) / dt; 33 display_game(interpolation); 34}

The frameskip bit is optional, as is interpolation. The critical bit here is that the FPS is limited by the vsync (which is (still!) a tiny bit buggy on A5) instead of using al_wait_for_event like in the more classical A5 game loops. You can also add a mechanism to skip frames if you want to keep the FPS lower than the refresh rate.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Kris Asick
Member #1,424
July 2001

Siege's loop format is actually very similar to mine, except I don't use Allegro for timing as I have my own timing system in place, so I process Allegro events first, then handle game logic.

...I also just noticed some errors in how I explained my loop format. *goes to fix them*

...meh, it won't let me because of the age of the post, so I'll just repost my pseudo-code with the fixes applied:

do {
    // 1. Update timer and calculate how many logic frames to process (if any)
    // 2. Handle All Allegro Events in Queue
    while (logicframes > 0)
    {
        // 3a. Process Keyboard/Mouse/Joystick I/O
        // 3b. Process 1 frame of Game Logic
        logicframes--;
    }
    // 4. Interpolate Object Positions for Rendering
    // 5. VSync and Render Frame
    Sleep(0); // For sake of Windows multi-tasking
} while (!done);

--- Kris Asick (Gemini)
--- http://www.pixelships.com

Gnamra
Member #12,503
January 2011
avatar

Thank you for all the examples and the explanations.

I am currently making a physics engine for my high school physics exam, and it is also the first time I've really tried to use time steps and I'm astonished. It has made all collision detection so much better, and the movement of objects is so much smoother. I think I'll be using one timer from now on, and two timers in portables.

Go to: