Game loop/message queue

Started by
5 comments, last by ChristianJames 17 years, 7 months ago
Hi there, Im currently working on a little 2d platform game in c#. I have done one before but that was in pure c which made the game loop very linear. From what i have read i see two things 1) The windows event driven objects 2) the game message pump/loop. Now i know that the events that occure are also sent to some message queue but i still have a hard time distinguishing between the 1 and 2 in relation to a game design. For instance, one of my ideas were to have a timer object that rised an event every 40 ms. Ill have the draw() method subscribed to that timer event to ensure a 25 fps. Were also thinking about a keyboard object to rise keystroke events which again triggers some movement of a player through the producer-subscriber concept. So basicly u already have alot of events/actions wired together. I then read about this message pump in the main game loop. To me it seams quite similar to the old game loop but instead of plawing through functions and FSM's we just wait till a message pops up on the queue, decode what message it is and then get the appropiate handler to take care of it. Now while i like the idea of the message pump I find it a bit obsolete since a lot of the events can be wired indirectly via. the producer-subscriber pattern. So I guess the two methods i see are: 1) Event handling via. producer-subscriber pattern. whitch is kinda of a distributed way of routing things togehter. I could see this being a mess to keep track of all the message handling. 2) The message pump, which is a more centralized approch. All messages go into one queue and are decoded and from there (the center) are handed out to the appropriate handlers. I would be really happy if someone could shred some light on this matter cause im very confused of what is atcually the best way or if the ycan be implemented nicely together.
Advertisement
Don't use the timer method, as trying to send a message every 40ms is unlikely to be reliable.

Simply run the loop as often as you can, timing how long each iteration takes. Within each iteration, process any Windows messages or other events that come in. Then update and render your world, passing in the iteration time as a scaling factor.

If you'd rather do without the scaling factor, limit your frame rate by waiting at the end of each iteration until a certain time has passed, but still handle every event available to you in each iteration.

If your game is real time then you can't practically use an entirely event-driven model for scheduling your updates. You just have to keep looping. Feel free to use events and messages for everything else, however.
I agree with Kylotan in regards to your rendering/update loops. Just run them as fast as you can. However, having a singelton type scheduling object that links to your game objects isnt a bad idea either. The event system of C# makes this trivial, and doing this like... say... AI updates based on a real world timer isnt such a bad idea.

The reason I say keep rendering/update calls in your main loop is, a timer based event system wont degrade worth a damn if for some reason, the game isnt able to keep up with the timer calls ( or vice versa). Running simply as fast as possible will scale better across various computers.
I'm with Serapth. I think the observer pattern can be incredibly useful in managing messages/commands passed back and forth between game objects. But I did want to caution you on handling input via raising events.

One of the advantages of using the observer pattern (or producer/subscriber as you call it) is that multiple observers can be registered to a single subject. So, for example, you could have your HUD, your player character, and your game world all registered to receive keyboard events. But there are some problems you'd run in to doing things like this.

First, you need a mechanism for "consuming" the event. What I mean is that if the HUD handles the event, you don't want the player character to interpret the event. As an example, let's say a text-edit box in your HUD/GUI is currently focused and the player presses the 'w' key. What you expect is that a 'w' is added to the text-edit box and that your player character does not move. But the player character receives the key input event because he is registered for it and the character moves. Now, handling this seems simple -- add a boolean flag to the EventArgs that marks whether the event has been "consumed." If it has, you ignore it. But that leads to a second problem...

It is difficult in this model to guarentee what order observers are called. The pattern itself is based on the principle that the order does not matter...on a more practical note, I do not know if C# makes any guarentees on the order (FIFO? LIFO? Unknown?). So taking the above example, what if the player character is called before the HUD/GUI? Then your character will move, consume the event, and the text box will not add the 'w' as you expect.

Even if your system does provide some guarentee on order (say, FIFO), it can become difficult to make sure that your observers are always added in the correct order. For example, when opening up the pause menu, you have to remove the current observers because they are called FIFO and the menu must be called first. Then when exiting the menu you need to make sure they are added back in the correct order. You could construct a similar situation if the order were always LIFO. You may be able to solve this by having your Subject automatically sort observers based on some sort of "priority" -- so your menu's priority is always higher than the HUD, which is always higher than the character, etc. Of course, if you ever need to swap the order in which observers are notified at run-time, this becomes even more problematic.

These problems tend to lead to having a single observer for these events that then passes them on to some "state" object that you define. That state can then make the proper decision on how to handle the event. E.g. it could ask the GUI to handle it, and the if the GUI does not, ask the character to. When in the "pause" state, it would only ask the menu to handle it. But now you've just given up one of the primary reasons for using observers in the first place.

There is also the problem of when input is handled. In games, the order that things occur can be incredibly important. For example, you probably don't want to handle input that moves your character after you have performed collision detection and physics. Otherwise your game may allow the character to pass through objects (atleast partially) until collision detection runs in the next frame which may lead to all sorts of odd bugs. So now your fancy event-driven input system has devolved in to:

// Updates state of the keyboard device and notifies the one// observer attached to it. That observer will forward all events// to a state object which routes them to the proper handlers.keyboard->ProcessInput()


Which is semantically no different than:

// Process input events since last framekeyboard->ProcessInput();// Get events and forward them on to the statewhile (keyboard->HasEvent())    currentState->HandleKeyboardEvent(keyboard->NextEvent());


Except the while loop is contained in your Keyboard class and executes while gather events. And the call to ask the current state to handle the event is in your observer class. Thus you are required you to write a class implementing an interface, allocate an instance of that class, and register it with the keyboard. Plus you have to make sure that class always contains the current game state so that it can forward to it. All this instead of two lines in your game loop.

So, my opinion is that when dealing with input, it is much simpler and easier to debug if you use polling as opposed to some fancy event-driven system.

I want to reiterate that the observer pattern is powerful and has it's uses...I just don't think processing input in a game is one of them.
Quote:Original post by penwan
...

That was a nice writeup - thanks.
Tadd- WarbleWare
From the man whom literally wrote the book on managed directx... a game loop:
https://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

Quote:Tom Miller
My last post on render loops (hopefully)..

The most common topic on my blog returns again. This time it will be brief as all I'm going to to do now is show you the render loop the June'05 SDK will be using. A coworker in another group came up with this markedly simple, yet deceptively effective loop for that groups projects. I liked it so much, i'm sharing it with everyone else. =)

The basic loop (slightly modified from his original version and the version in the new SDK for ease of reading):


public void MainLoop(){        // Hook the application's idle event        System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);        System.Windows.Forms.Application.Run(myForm);}private void OnApplicationIdle(object sender, EventArgs e){    while (AppStillIdle)    {         // Render a frame during idle time (no messages are waiting)         UpdateEnvironment();         Render3DEnvironment();    }}private bool AppStillIdle{     get    {        NativeMethods.Message msg;        return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);     }}//...And the declarations for those two native methods members:[StructLayout(LayoutKind.Sequential)]public struct Message{    public IntPtr hWnd;    public WindowMessage msg;    public IntPtr wParam;    public IntPtr lParam;    public uint time;    public System.Drawing.Point p;}[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously[DllImport("User32.dll", CharSet=CharSet.Auto)]public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);


Quote:Tom Miller
Simple, elegant, effective. No extra allocations, no extra collections, it just works.. The Idle event fires when there's no messages in the queue, and then the handler keeps looping continuously until a message does appear, in which case it stops.. Once all the messages are handled, the idle event is fired again, and the process starts over.


EDIT: Though because of the pinvoke, that won't be any good cross platform.
ZBuffer has an excellent synopsis of the game loop topic:
http://www.thezbuffer.com/articles/185.aspx

[Edited by - paulecoyote on September 21, 2006 3:17:11 PM]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Thanks alot everyone.
Some very interesting scenarios u pointed out there penwan. I'll definitely not be using the event driven user input now seing how much truble it involves. However i guess that instead of the observer pattern u could use the chain of command pattern instead. This way u would insure that only one 'subscriber' handles the event plus u know the order of whitch the event is passed down.

This topic is closed to new replies.

Advertisement