On topic for once...
I have found that writing the main event loop is not trivial and there are several event-getting functions that Allegro has.
In the demos and examples they use different ways.
Speed
Skater
Cosmic Protector
I think this might be confusing for people starting out.
Is there a 'best' way to do it (for programs that don't have unusual requirements) and if so, should we emphasize it in the docs/examples/demos?
Let's distill down the essence of each loop, shall we?
From your first link for Speed : https://github.com/liballeg/allegro5/blob/6017bed18585146d2499aa0a13709f975a9bb047/demos/speed/main.c#L67-L129
We have this :
Let's analyze that. The basic form is this :
while (!gameover) { while ((al_get_timer_count(inc_counter) > 0) && (!gameover)) { /* LOGIC */ } if (redraw) { draw_view(); redraw = 0; } else { rest(1); } }
How is that any different than the old A4 style game loop?
while (keepgoing) { while (ticks--) { Logic(); } if (redraw) { Redraw(); redraw = false; } rest(1);
That's a horrible way to code a game loop.
Let's move on.
Example 2, Skater demo : https://github.com/liballeg/allegro5/blob/6017bed18585146d2499aa0a13709f975a9bb047/demos/skater/src/framework.c#L256-L464
Basically, it boils down to this :
That's ugly, and it depends on a bunch of globals.
Basically, it's this :
Which is much cleaner and easier to understand. It's your basic A5 game loop.
Third, we have Cosmic Protector : https://github.com/liballeg/allegro5/blob/6017bed18585146d2499aa0a13709f975a9bb047/demos/cosmic_protector/src/logic.cpp#L52-L67
And, I can't follow that or even find main, so I'm not gonna try.
Skater wins.
Actually, the SPEED loop is an Allegro 4 loop. The original was written for a Speed Hack by Shawn himself. To include it as an example to the newer Allegro they wrote a sort of "wrap layer" to simulate Allegro 4, defined in file a4_aux.h and implemented in file a4_aux.c. It is an example about how to implement an old-school game system.
Also, you have more ways to work with the game loop. Since my game engine is component based, my game loop is quite simpler than those, but of course it needs a lot of "behind the scene" work.
SourceForge doesn't have a fancy way to show code, so I just copypaste it here from mnggame.pas:
Simple explanation, each component that needs Allegro events just registers itself into the fEventManager and the Poll method just manages it. The fTimer object, for example, is one of these components, and it not just updates the clock but also calls the appropriate methods to update the game and render the stuff on screen. But it is quite more complex actually.
Anyway, I think there's no one best way to do it.
I don't suppose there's a single best way that can be copied and pasted into every project. But if you look at what Edgar did, he was able to distill out the essence of each alternative.
In Cosmic Protector, the main loop looks like
repeat { delta = time_since_last_frame logic(delta) render(delta) pause for 1/60 second }
and the 'logic' is
while (event queue not empty) { get event process event }
and the only events it's interested in are display close and switch out.
That timer-less approach is quite common in code I have seen.
Most code seems to use variations on the Skater loop, which uses a timer.
repeat { repeat { get event if timer event then update = true } until (queue is empty) if update { logic redraw } }
(with the possible refinement of handling > 1 timer event per batch of events)
There are also al_wait_for_event_timed and al_wait_for_event_until which I don't think I have seen used anywhere, but they ought to be usable to control timing in an event loop.
Finally there are the bad ones
repeat { get event process event logic redraw }
and
repeat { wait for event process event logic redraw }
which need no further discussion!
EDIT:
Coincidentally this appeared on the front page of Hacker News this morning but I haven't been bothered to read it yet.
http://gameprogrammingpatterns.com/game-loop.html
What about frame skipping and different logic and rendering rates? We should discuss those too.
Logical frame skipping is simple. Only run one update, even if there are multiple timer events. Ignoring them will make your game slow down gracefully and you will see every update on the screen. However, sometimes you don't want your game to slow down. You can run multiple logic updates per render call, and then your game will run the same speed on every computer if you're using a timer. However, this may lead to teleportation if it takes longer to process logic than you have time available after setting a rendering rate.
My personal preference is to drop all timer events after the first. I'd rather the game slow down if the computer can't handle it. Then I can see whether or not my hardware is good enough to run it.
What about frame skipping and different logic and rendering rates? We should discuss those too.
Yes; some games (maybe the more 'vector-ish' ones, like flight sims, might suit that)
Also, in the code above
do { ALLEGRO_EVENT event; al_wait_for_event(event_queue, &event); HandleEvent(event); } while (!al_is_event_queue_empty(event_queue));
is there a chance that the queue will never be empty (say if you whizzed the mouse about continually) and so the loop will never run?
A mouse has a refresh rate up to 1000hz, I don't think a mouse event handler couldn't run under 0.001 seconds. I suppose if some of the event handlers do take a lot of time the loop could potentially not finish.
I've never been able to whizz the mouse around fast enough to make the rendering pause or stutter. Don't think it's likely under normal circumstances.
If you were feeling paranoid, you could set a limit on the max number of events to handle at once, but then you get events piling up sometimes.
I know if you try to render once for every mouse event you're gonna feel the pain, because most screens don't update as fast as the mouse.