🔒 Locked
Simple game loop question
Advertisement
Original Post
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?
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.
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.
My main routine looks like this:
The window.ProcessMsg() call, is the win32 message pump required to keep your window happy, if it returns zero then the app is closing.
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.
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
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.
check nehe tuts for more code.
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.
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.
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.
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.
Here is "my" loop(comments are from the original article):
If it behaves exactly the same, then why does it have more potential?
/** 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?
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:
Just a bit nit-picking:
This one is nicely compact, avoiding unneccessary jumps. It checks the "running" argument only if the current inner iterations are totally processed.
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.
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.
oh i see. still id use GetAsyncKeyState over windows messages for input.
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.
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.
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.
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.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement