9
\$\begingroup\$

I've read that the speed of game objects should not be hindered by FPS but instead should be based on time. How can I seperate the update/draw code to maximize performance without limiting the drawing rate and provide a constant logic update rate based on time?

My current pseudo code is as follows

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

The problem is the drawing code hinders the performance of the update() rate. And it consumes 100% cpu because if sleep is thrown in, it throws off both drawing/logic functions.

I am also using SDL and it doesn't seem to have a vsync option. I've also heard of the terms fixed and variable time-stepping however I'm not sure how that can be done with sleep()

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You don't need to waste 100% CPU power just for waiting, put a sleep(0) at the end of the while loops if ticksElapsed() < 100. The OS will return to the thread immediately if there is no other thread that wants to run. But not wasting 100% CPU power anymore. \$\endgroup\$ Jul 10 '11 at 14:56
  • \$\begingroup\$ However, best solution for such a 1 thread setup is to use vsync, if you cant vsync, then call sleep(0) in a loop until you reached the target frame rate, then update and draw \$\endgroup\$ Jul 10 '11 at 15:09
3
\$\begingroup\$

In your code snippet it looks like you're trying to run your game in fixed-time step mode by busy-waiting if your drawing and updating took less then 15ms (60fps). This is possible and you guessed right that this can't be done using a sleep call because you don't exactly know how long you're going to sleep. The busy-waiting-loop is the good solution.

However consider the case where your updating and drawing exceed 15ms, you now have the game drawing and updating to slow. You can now do either two things: detect this state and drop frames (skip drawing and go straight to updating until you are in sync again) however if the computer is just to slow it will never catch up.

An other solution is to make your update logic fixed-time independent. You don't need a separate thread for this, you just have to respecify how fast things should move. Instead of 5pixels per tick, you should use 50pixels per second. You would need a high precision timer to achieve this, and all your update logic should be able to access the timer to see how much time passed since last update.

Basically you go from:

void UpdatePlayer()
 player.x += 10;

To

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
\$\endgroup\$
11
  • \$\begingroup\$ So my engine will always consume 100% and there's nothing I can really do about it? \$\endgroup\$ Jul 9 '11 at 19:48
  • 1
    \$\begingroup\$ It will consume as much cycles as the scheduler allows it to get, but that's not really an issue since you can't really do a lot of other things while you're playing a game :). \$\endgroup\$
    – Roy T.
    Jul 10 '11 at 7:26
  • 1
    \$\begingroup\$ @Roy sleep(0) is the solution. It returns immediately if there is no other thread that wants to run (Sleep WinAPI) and gives other threads the chance to run. If the other thread will not give the main thread the chance to run in return, then you have a threading problem, but blocking everything else by not calling sleep in the first place makes it even worse and is hardly a solution. The key is to call sleep(0) and test the elapsed time until you hit your target fixed frame rate, so you dont waste 100 % CPU just for waiting. \$\endgroup\$ Jul 10 '11 at 14:46
  • 1
    \$\begingroup\$ @Maik Hmm, OK that last argument got me! You're right ofc the OS can transfer your process from running to ready anytime, so yes sleep(0) would be better :). So advice for the one who asked the question would be: use sleep(0) not sleep(15ms). \$\endgroup\$
    – Roy T.
    Jul 11 '11 at 10:37
  • 1
    \$\begingroup\$ @Roy I would say so too :) one should never expect the timing of sleep to be exactly, the number passed as parameter is more like a hint to the scheduler, the only assertation is, that is doesnt come back earlier. So yeah, best use 0 as param, that tells the scheduler, "get back as soon as possible", which is usually less than small fractions of milliseconds \$\endgroup\$ Jul 11 '11 at 11:29

You must log in to answer this question.

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