• Advertisement
Sign in to follow this  

Simple game loop question

This topic is 4220 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have always used glut for my openGL applications and I could just pass it a function to render and it always did it's thing. I'm trying to make something without glut this time. So how is the game loop usually created? Do you just fire off a new thread that constantly calls the render and logic code?

Share this post


Link to post
Share on other sites
Advertisement
You don't need to fire up a thread for your game loop if you don't want to. A game loop is just that, a game loop, and you summed it up pretty well actually, except that you'd also want to check for user input.
I've seen some designs that created a thread for their current active object (e.g. a menu, or the game object with it's loop). In my own game, I've put the main loop in the game manager class, which is created and started from within the main function - no threading involved. It depends on your design, so far I haven't investigated deep enough to say which one is better.

Share this post


Link to post
Share on other sites
My main routine looks like this:


int main()
{
bool fullscreen = true;

int ret;

if (MessageBox(NULL,"Message", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE;
}

if (!window.Create(16, 800, 600, "Title", fullscreen))
{
Msg("couldnt load window");
return 0;
}

Init();

window.Show(true);

ShowCursor(false);
while (!exitgame)
{
ret = window.ProcessMsg();
if (ret == 0)
break;
if (ret == 1)
DoFrame();
}

DeInit();

ShowCursor(true);

window.Destroy();
return 0;
}




The window.ProcessMsg() call, is the win32 message pump required to keep your window happy, if it returns zero then the app is closing.

Share this post


Link to post
Share on other sites

My main function looks like this...


int main(int argc, char** argv)
{
Application app;
app.CreateWindow("The Demo",800,600, /*fullscreen=*/ false);
app.Initialize();
while (app.isRunning)
app.Run();
app.DeInitialize();
return 0;
}



Application::Run handles all the rendering and input checking

Share this post


Link to post
Share on other sites
The main loop shouldnt be in a seperate thread to my opinion, that would be a waste, what you want is as little threads as possible.

A basic gameloop looks something like this.


void main()
{
bool running = true;
MSG msg;

while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//render stuff here
}
}
}




check nehe tuts for more code.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by ronkfist
A basic gameloop looks something like this.

while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//render stuff here
}
}


I do not recommend such loops. See, between two consecutive frames only one
message is processed. It works well on high framerates, like in simple demos.
If you get ~25fps, you only process 25 messages per second. I couldnt believe
it, but such loops happen in professional commercial games! When you click
a mouse, you generate two messages, LBUTTONDOWN and LBUTTONUP. Thats a ~1.5
frame reaction, when you could have ~0.5 in this loop:


while (running)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//render stuff here
}


Don't be afraid of while loops. It will process at most few tens of messages
in a no-time.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Quote:
Original post by ronkfist
A basic gameloop looks something like this.

while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//render stuff here
}
}


I do not recommend such loops. See, between two consecutive frames only one
message is processed. It works well on high framerates, like in simple demos.
If you get ~25fps, you only process 25 messages per second. I couldnt believe
it, but such loops happen in professional commercial games! When you click
a mouse, you generate two messages, LBUTTONDOWN and LBUTTONUP. Thats a ~1.5
frame reaction, when you could have ~0.5 in this loop:


while (running)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//render stuff here
}


Don't be afraid of while loops. It will process at most few tens of messages
in a no-time.


Take a good look...
The one I posted will behave exactly the same way as yours and has more potential.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Programmer16
Quote:
Original post by ronkfist
Take a good look...
The one I posted will behave exactly the same way as yours.


No, yours will process A message if one is there while his will process ALL of the messages.


his loop doesn't render anything if there is a message though, so it works just as a while loop.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Quote:
Original post by Programmer16
Quote:
Original post by ronkfist
Take a good look...
The one I posted will behave exactly the same way as yours.


No, yours will process A message if one is there while his will process ALL of the messages.


his loop doesn't render anything if there is a message though, so it works just as a while loop.


No, his will only stop for 1 message and then render while the other will stop until there are no messages.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Programmer16
Quote:
Original post by Anonymous Poster
Quote:
Original post by Programmer16
Quote:
Original post by ronkfist
Take a good look...
The one I posted will behave exactly the same way as yours.


No, yours will process A message if one is there while his will process ALL of the messages.


his loop doesn't render anything if there is a message though, so it works just as a while loop.


No, his will only stop for 1 message and then render while the other will stop until there are no messages.


read his code again :)

while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//render stuff here
}
}



note the ELSE..
if there is a message it will NOT render anything and simply start over again with if (PeekMessage...

Share this post


Link to post
Share on other sites
Here is "my" loop(comments are from the original article):
/** inspired by http://www.flipcode.com/cgi-bin/fcarticles.cgi?show=63823 */
/** Main Loop with Fixed Time Steps by Javier Arevalo */
void Game::loop(const LL_DWORD TICK_TIME, const int MAX_LOOPS) {
LL_DWORD time0;
LL_DWORD time1;
LL_DWORD frameTime;

m_running = true;

LMINFO("Entering gameloop");

time0 = getTickCount();
do {
time1 = getTickCount();
frameTime = 0;
int numLoops = 0;

/*
The concept behind this is forcing every game logic tick to represent a
fixed amount of real-time (real-time is the time shown on your wristwatch).
Of course, ticks better take less CPU time to execute, than the real-time
it represents.
*/

while((time1-time0) > TICK_TIME && numLoops < MAX_LOOPS) {
gameTickRun(TICK_TIME/LL_REAL_VALUE(1000.0));
time0 += TICK_TIME;
frameTime += TICK_TIME;
numLoops++;
// Could this be a good idea? We're not doing it, anyway.
// time1 = getTickCount();
}

/*
This is where we gather user input and update the whole user interface
and perform general housekeeping. :) Things that don't need to be run at
a precise real-time speed. Since we want our game logic to be perfectly
deterministic, the things executed here (or during rendering) must not
affect the fixed-step game logic, in any way. In fact we have a run mode
where we record the user commands to a file, and we can play back these
commands entirely within the timestep loop above. This is an invaluable aid
for debugging, and is the basis for the lockstep network model we employ.
*/

if( frameTime != 0 ) {
independentTickRun(frameTime/LL_REAL_VALUE(1000.0));
}

// if playing solo and game logic takes way too long, discard pending time
if( !m_isNetworkGame && (time1-time0) > TICK_TIME ) {
time0 = time1 - TICK_TIME;
}

/*
This litte thing allows us to interpolate the positions, orientations,
etc of the visuals, in between game ticks. It smooths the visual aliasing
caused by a non-constant framerate. Which is the case, because we run a game
logic tick 15 times per second (TICK_TIME = 1000/15), while displaying at
framerates in the range 20-60 frames per second (fingers crossed).
*/

if(m_canRender) {
// account for numLoops overflow causing percent > 1
LL_REAL percentWithinTick = lunarlady::math::min(LL_REAL_VALUE(1.0), LL_REAL(time1-time0)/TICK_TIME);
drawWithInterpolation(percentWithinTick);
}
} while ( !m_stateManager.empty() && m_running);
}



Quote:
Original post by ronkfist
Take a good look...
The one I posted will behave exactly the same way as yours and has more potential.

If it behaves exactly the same, then why does it have more potential?

Share this post


Link to post
Share on other sites
Well in this case it does exactly the same thing. With more potential I simply meant that you can easier add conditional code in case it's neccessary.

Share this post


Link to post
Share on other sites
Quote:
Original post by ViperG
just remove the else statement.


No, that would be a bad idea, as Anonymous pointed out..
Quote:
Original post by Anonymous Poster
See, between two consecutive frames only one
message is processed. It works well on high framerates, like in simple demos.
If you get ~25fps, you only process 25 messages per second. I couldnt believe
it, but such loops happen in professional commercial games! When you click
a mouse, you generate two messages, LBUTTONDOWN and LBUTTONUP. Thats a ~1.5
frame reaction, when you could have ~0.5 in this loop:

Share this post


Link to post
Share on other sites
Just a bit nit-picking:


while (running)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//render stuff here
}


This one is nicely compact, avoiding unneccessary jumps. It checks the "running" argument only if the current inner iterations are totally processed.


while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//render stuff here
}
}


This one wastes a (neglectible) amount of time due to its "else" branch and repeating "if" clause. On the other hand, it checks the "running" argument on each iteration, and not only if no more messages are pending.

So the both versions behave not necessarily the same when it comes to exiting the loop.

Share this post


Link to post
Share on other sites
Ronkfist - how can yours possibly process more than one message per frame?

Share this post


Link to post
Share on other sites
oh i see. still id use GetAsyncKeyState over windows messages for input.

Quote:
Original post by Ezbez
Ronkfist - how can yours possibly process more than one message per frame?


The while statment.

the if statement will only check 1 msg, then renders.

the while statement keeps processing msg after msg until there are no more msgs, then it renders when msgs are all processed.

Share this post


Link to post
Share on other sites
But that's still only on message per frame, there. If two messages are sent during the time of the frame, it will only respond to one of them.

Share this post


Link to post
Share on other sites
well from what i understand windows msgs are a queue system, so you can have 5 msgs waiting at once, and with the if statement, it only checks 1 at a time. a while statement will check all 5 in 1 gameloop.

again it only happens at low framerates, like 30 or below where you might get stacked messages. if your doing over 60 fps windows most likely wont send more than 1 per frame at you.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
My bad, ronkfist's loop is correct. The "else" statement does the job.
I apologize. Still, it does not change the fact, that processing one message per frame is not a good idea.
I can be wrong, but I believe Far Cry is processing messages that way. It is most noticeable in the game menu, when you click on buttons and other items. I am going to check it out.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement