34
\$\begingroup\$

I'm currently beginning to learn OpenGL at school, and I've started making a simple game the other day (on my own, not for school). I'm using freeglut, and am building it in C, so for my game loop I had really just been using a function I made passed to glutIdleFunc to update all the drawing and physics in one pass. This was fine for simple animations that I didn't care too much about the frame rate, but since the game is mostly physics based, I really want to (need to) tie down how fast it's updating.

So my first attempt was to have my function I pass to glutIdleFunc (myIdle()) to keep track of how much time has passed since the previous call to it, and update the physics (and currently graphics) every so many milliseconds. I used timeGetTime() to do this (by using <windows.h>). And this got me to thinking, is using the idle function really a good way of going about the game loop?

My question is, what is a better way to implement the game loop in OpenGL? Should I avoid using the idle function?

\$\endgroup\$
3
  • \$\begingroup\$ gamedev.stackexchange.com/questions/651/… Duplicate? \$\endgroup\$ Commented Feb 18, 2011 at 9:28
  • \$\begingroup\$ I feel this question is more specific to OpenGL and whether using GLUT for the loop is advisable \$\endgroup\$
    – Jeff
    Commented Feb 18, 2011 at 22:29
  • 1
    \$\begingroup\$ Here man, don't use GLUT for larger projects. It is fine for smaller ones, though. \$\endgroup\$
    – bobobobo
    Commented May 5, 2013 at 23:52

2 Answers 2

30
\$\begingroup\$

The simple answer is no, you do not want to use the glutIdleFunc callback in a game that has some sort of simulation. The reason for this is that this function divorces animation and draw code from window event handling but not asynchronously. In other words, receiving and handing window events stalls draw code (or whatever you put in this callback), this is perfectly fine for an interactive application (interact, then response), but not for a game where the physics or game state must progress independent of interaction or render time.

You want to completely decouple input handling, game state, and draw code. There is an easy and clean solution to this that does not involve the graphics library directly (i.e. it's portable and easy to visualize); you want the entire game loop to produce time and have the simulation consume the produced time (in chunks). The key however is to then integrate the amount of time your simulation consumed into your animation.

The best explanation and tutorial I have found on this is Glenn Fiedler's Fix Your Timestep

This tutorial has the full treatement, however if you do not have an actual physics simulation, you can skip the true integration but the basic loop still boils down to (in verbose pseudo-code):

// The amount of time we want to simulate each step, in milliseconds
// (written as implicit frame-rate)
timeDelta = 1000/30
timeAccumulator = 0
while ( game should run )
{
  timeSimulatedThisIteration = 0
  startTime = currentTime()

  while ( timeAccumulator >= timeDelta )
  {
    stepGameState( timeDelta )
    timeAccumulator -= timeDelta
    timeSimulatedThisIteration += timeDelta
  }

  stepAnimation( timeSimulatedThisIteration )

  renderFrame() // OpenGL frame drawing code goes here

  handleUserInput()

  timeAccumulator += currentTime() - startTime 
}

By doing it this way, stalls in your render code, input handling, or operating system do not cause your game state to fall behind. This method is also portable and graphics library independent.

GLUT is a fine library however it is strictly event-driven. You register callbacks and fire off the main loop. You always hand over control of your main loop using GLUT. There are hacks to get around it, you can also fake an external loop using timers and such, but another library is probably a better (easier) way to go. There are many alternatives, here are a few (ones with good documentation and quick tutorials):

  • GLFW which gives you the ability to get input events inline (in your own main loop).
  • SDL, however its emphasis is not specifically OpenGL.
\$\endgroup\$
6
  • \$\begingroup\$ Good article. So to do this in OpenGL, I wouldn't use the glutMainLoop, but instead my own? If I did that, would I be able to use the glutMouseFunc and other functions? I hope that makes sense, I'm still pretty new to all this \$\endgroup\$
    – Jeff
    Commented Feb 17, 2011 at 18:26
  • \$\begingroup\$ Unfortunately not. All callbacks registered in GLUT are fired off from within glutMainLoop as the event queue is drained and it iterates. I added some links to alternatives in the answer body. \$\endgroup\$
    – charstar
    Commented Feb 18, 2011 at 9:07
  • 1
    \$\begingroup\$ +1 for ditching GLUT for anything else. As far as I know, GLUT was never meant for anything but test applications. \$\endgroup\$ Commented Feb 18, 2011 at 9:19
  • \$\begingroup\$ You still have to handle system events though, no? My understanding was that by using glutIdleFunc, it just handles the (in Windows) GetMessage-TranslateMessage-DispatchMessage loop and calls your function whenever there is no message to be gotten. You'd do that yourself anyway, or else suffer the OS's impatience in flagging your application as unresponsive, right? \$\endgroup\$
    – Ricket
    Commented Feb 18, 2011 at 13:54
  • \$\begingroup\$ @Ricket Technically, glutMainLoopEvent() handles incoming events by calling the registered callbacks. glutMainLoop() is effectively { glutMainLoopEvent(); IdleCallback(); } A key consideration here is that, redrawing is also a callback handled from within the event loop. You can make an event-driven library behave like a time-driven one, but Maslow's Hammer and all that... \$\endgroup\$
    – charstar
    Commented Feb 18, 2011 at 22:51
4
\$\begingroup\$

I think glutIdleFunc is just fine; it's essentially what you'd end up doing by hand anyway, if you didn't use it. No matter what you're going to have a tight loop which is not called in a fixed pattern, and you need to make the choice whether you want to convert it to a fixed-time loop or keep it a variable-time loop and make sure all your math accounts for interpolating time. Or even a mix of these, somehow.

You can certainly have a myIdle function which you pass to glutIdleFunc, and within that, measure the time that has passed, divide it by your fixed timestep, and call another function that many times.

Here is what not to do:

void myFunc() {
    // (calculate timeDifference here)
    int numOfTimes = timeDifference / 10;
    for(int i=0; i<numOfTimes; i++) {
        fixedTimestep();
    }
}

fixedTimestep would not be called every 10 milliseconds. In fact it would run slower than real time, and you might end up fudging numbers to compensate when really the root of the problem lies in the loss of the remainder when you divide timeDifference by 10.

Instead, do something like this:

long queuedMilliseconds; // static or whatever, this must persist outside of your loop

void myFunc() {
    // (calculate timeDifference here)
    queuedMilliseconds += timeDifference;
    while(queuedMilliseconds >= 10) {
        fixedTimestep();
        queuedMilliseconds -= 10;
    }
}

Now this loop structure ensures that your fixedTimestep function would get called once per 10 milliseconds, without losing any milliseconds as remainder or anything like that.

Due to the multitasking nature of operating systems you can't rely on a function being called exactly every 10 milliseconds; but the above loop would happen quick enough that it would hopefully be close enough, and you can assume that each call of fixedTimestep would increment your simulation 10 milliseconds worth.

Please also read this answer and especially the links provided in the answer.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .