Better game loops than endless loop + block?

2020-06-04 04:00发布

Every game tutorial and game framework (even the rather new XNA framework) start off with a never ending loop that has an equivalent of DoEvents() to prevent the OS from locking up.

Comming from a non-game based perspective I feel this kind of code smells rather funky.
Are there no better alternatives?

--EDIT--
A lot of answers say every program is basically a loop. True, but I feel the loop should be performed by your OS, not by you. Only the OS has all the information it needs to distribute its resources in an optimal way. Or am I missing an important point here?

9条回答
Bombasti
2楼-- · 2020-06-04 04:02

Message Queues are Infinite loops as well which makes all the programs as Infinite Loops.

查看更多
够拽才男人
3楼-- · 2020-06-04 04:07

Every Windows app has at its core a loop that looks something like this:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 ) 
{
   if (bRet == -1 )
   {
      // handle the error and possibly exit
   }
   else
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
   }
}

It's the application's job--not the operating system's--to ensure that messages are dispatched.

As you probably know, early Windows games used an alternate method where, instead of calling the blocking GetMessage function, they'd call PeekMessage, and then call the game's main processing loop if there was no message to handle. Various forms of delays were used to try to get an adequate frame rate without taking 100% CPU. There just wasn't a good enough timer to give a smooth frame rate.

Today, it might not be necessary to explicitly write a loop that calls DoEvents. It might be possible now to get a smooth frame rate by using the built-in timer pool timers (exposed in .NET by System.Threading.Timer, and wrapped by System.Timers.Timer).

As I recall, there were also issues with getting mouse and keyboard events in a timely manner. We used direct keyboard and mouse access rather than depending on the message loop, because the message loop was often too slow and sometimes would cause us to lose information.

I've not written games in a number of years, and I don't know what .NET is like as a games platform. It's possible that input is still a problem--that the message queue simply isn't fast enough to give the lightning-quick response that game developers want. So they bypass the message queue for critical tasks.

查看更多
一纸荒年 Trace。
4楼-- · 2020-06-04 04:11

I don't see how it could be done much differently?

Your program execution happens inside a thread (or multiple threads if you're fancy). Having a gameloop (or a win32 GetMessage/DispatchMessage pump) is all about waiting for event to happen and then handle them. The alternative would be registering callbacks, then calling an HandleAllMyMessages(), which would internally do pretty much the same thing... and that wouldn't really buy you anything.

The OS isn't going to be able to magically split your program into multiple threads, since it has no knowledge of what you want to do within those threads. Spawning a new thread for every incoming message would be both performance and synchronization hell.

查看更多
我想做一个坏孩纸
5楼-- · 2020-06-04 04:12

On Win32 I use a MultiMedia timer to call my gamelogic tick...

// request 1ms period for timer
g_mmTimer = timeBeginPeriod(1); 
if (g_mmTimer != TIMERR_NOERROR)
    return false;

//start game loop event timer/thread
g_mmTimer = timeSetEvent(10, 10, DoUpdate, 0, TIME_PERIODIC );

Optionally, you can do all the drawing in another thread so that the game logic is automatically independent of frame rate.

查看更多
我只想做你的唯一
6楼-- · 2020-06-04 04:18

Games (in most cases) are simulations. Traditionally this means you update the simulation, present its current state (eg. render graphics), and repeat. The natural representation for this is a loop.

The Win32 message pump on the other hand is a platform specific quirk, geared towards the event-driven applications that make up the majority of apps on Windows. It's by no means the standard for applications across all platforms so it's not surprising that not all software fits nicely into the model. So to amend a typical program to fit the Win32 model, you typically drain that queue in one go, once per iteration of the loop, using PeekMessage until it's empty. Or, you put that logic into a separate thread and use GetMessage as appropriate.

For most games with performance in mind there is no other practical way to do this. Firstly, if you tried to make the game event-driven instead of polled you'd need higher resolution timing than Windows can reliably give you if you want to keep the high performance that many games go for. Secondly, Windows is only one of the platforms that games are written for, and reworking the game to fit nicely into the Win32 model will just be an inconvenience for dedicated game platforms which expect the canonical game loop instead.

Finally, concerns about 'taking 100% CPU' are misplaced. Most modern games are designed to be used in fullscreen mode with exclusive hardware access, not to be just one of several other applications co-existing. Customers of such games actually demand that the game makes the most use of their hardware and this cannot be done if there are deliberate Sleep() calls or updates are dependent on external timers waking the application N times a second. Obviously there are exceptions for many games, eg. those that are designed to run primarily in a window, but it's important to note the distinction.

查看更多
走好不送
7楼-- · 2020-06-04 04:24

All programs that are running are never ending loops. Though in some cases you don't interact with the loop (like in web programming or other minor programs)

Your OS is a loop waiting for input commands. Same with a game. A web server is a loop waiting for requests.

A great explanation is from the user slim on one of my own questions.

查看更多
登录 后发表回答