C:Beating the Message Pump

From GDWiki

Jump to: navigation, search

For those of us who coded under DOS, life was quite simple. The OS didn't get in the way of our game, we could adopt a simple approach to laying out our code:

GameLoop()
 {
  int Running=TRUE;
 
   while(Running)
    {
     GetInput();
     DoGameLogic();
     UpdateActors();
     Render();
    }
 
 }

Under Windows we have to deal with the message pump. We have to constantly check to see if the OS is sending us any messages.

The normal form to handle the pump is:

while(GetMessage(&msg,NULL,0,0))
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }

If we insert our game functions within this loop, the game will only update when Windows messages come in.

// Not a good idea
 
  while(GetMessage(&msg,NULL,0,0))
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
 
    GetInput();
    DoGameLogic();
    UpdateActors();
    Render();
   }

We often use PeekMessage() to pass over the message pump if there are no messages.

while(Running)
   {
     if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
      {
        // Check for a quit message
        if( msg.message == WM_QUIT )
         break;
       TranslateMessage(&msg);
       DispatchMessage(&msg);	
      }
     else
      {
       GetInput();
       DoGameLogic();
       UpdateActors();
       Render();
      }
   }

This allows for constant processing but we still have Windows in our face and the prog will run flat out, eating all the CPU cycles even when minimised.

To get back to a nicer DOS-like approach we can use a separate thread to run our game:

DWORD tID; // Thread Identifier
 
  // Create our game thread
  if(CreateThread(NULL,0,GameProc,0,0,&tID)==NULL)
   {
    MessageBox(NULL,"Create Game Thread Failed!","Error",MB_OK);
    return 0;
   } 
 
  // Normal message pump
  while(GetMessage(&msg,NULL,0,0))
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }

Now we can place all our game code in a separate thread:

DWORD WINAPI GameProc(LPVOID StartParam)
 {
  bool Running=TRUE;
 
   while(Running)
    {
     // Game code goes here
 
     // Set Running to FALSE to exit the thread
    }
 
  return 0;
 }

The message pump continues to work when required and our game thread doesn't have to worry about Windows.

When GameProc() isn't doing anything the whole app idles nicely and allows other processes to get at the CPU. We can force idling from the main Winproc using SuspendThread() and ResumeThread().

Personal tools
Categories