A discussion about fixed time steps

Started by
20 comments, last by Dmytry 17 years, 10 months ago
I've recently encountered a problem in my game and i'm not sure how to fix it. As you know, most physics engines use equation solvers that assume a constant time step in order for the simulation to be stable and consistent. Most beginners incorrectly tie their rendering framerate to their physics framerate:

float curTime = getTime();
while (!quit)
{
  float newTime = getTime();
  float elapsed = curTime - newTime;
  curTime = newTime;
  do_physics(elapsed);
  do_render(elapsed);
}
... which leads to unstable behaviors. Of course, assuming a constant time step:

float curTime = getTime();
const float timeStep = 1.0f / 100.0f; // 100 Hz
while (!quit)
{
  float newTime = getTime();
  float elapsed = curTime - newTime;
  curTime = newTime;
  do_physics(timeStep);
  do_render(elapsed);
}
... is even worse, since now, you're renderer-framerate dependent. So far i've been using a better approach, that i think pretty much everybody use now, using an accumulator:

float curTime = getTime();
const float timeStep = 1.0f / 100.0f; // 100 Hz
float accTime = 0; // accumulated time step
while (!quit)
{
  float newTime = getTime();
  float elapsed = curTime - newTime;
  curTime = newTime;
  accTime += elapsed;
  while (accTime > timeStep)
  {
    accTime -= timeStep;
    do_physics(timeStep);
  }
  do_render(elapsed);
}
Unfortunately i'm getting fundamental problems with it, too. The problem with it is that it behaves "well" as long as the performance is high. But as soon as the framerate goes down, OR if the time to execute this "do_physics" call is too high, it enters into an infinite loop: because at frame N-1 the value of "elapsed" was high, it increases the number of calls to "do_physics", which in turn will reduce the performance and increase the value of "elapsed" in the next frame. See a trend ? Once the code is starting to fall into that never-ending spiral, your program is basically freezing. Of course, adding a limit to the number of time steps allowed per frame doesn't help to avoid falling into that spiral in the first place, and you get single-digits framerate while your rendering framerate has theorically returned to "normal" since long. I'm looking for suggestions to fix this problem, and avoid the performance effect onto the time step.. Y.
Advertisement
I've used the method in Gaffer's articles with good effect, even under low frame rates (~10 FPS). I don't know how your way differs, but maybe it can help you out.

http://www.gaffer.org/articles/Timestep.html

Hope this helps,
Pete
Reading over your post again, it seems that the problem is that you can't guarantee that on average your do_physics takes less time than your timestep, which is an unwritten requirement for fixing the timestep.

I can only think of two solutions:
1. Optimise do_physics to take less than a 100th of a second.
2. Reduce your update rate, to say 50/second instead of 100, depending on your preference.

With complex physics becoming more present in modern games, a good solution to this problem would be very handy. Anyone got any other ideas?

Pete
Quote:Original post by siaspete
I've used the method in Gaffer's articles with good effect, even under low frame rates (~10 FPS). I don't know how your way differs, but maybe it can help you out.

http://www.gaffer.org/articles/Timestep.html

Hope this helps,
Pete


And you did not suffer from the problem i described ? When your framerate starts to slow down, it makes the number of physics steps explode..

I've already read that article you read to. I find it interesting that he says you should understand all the "subtleties", calls his technique "perfect" and recommends it for "all game systems" ...

Y.
Clearly, you are in trouble when the physics takes more real time to compute than the virtual physics dt update that you get from physics.

Basically, to detect such a situation you can try doing something like this:
double physics_dt=0.01;double k=0.5;// some arbitrary constant, specifying how much physics step must decrease lag , at minimum. double real_time=CurrentTime();double virtual_time=real_time;while(playing){ double lag=real_time-virtual_time;// additionally, you can check lag in loop doing sleep(0) // until lag becomes greater than physics_dt.// to avoid doing re-renders when the state is not changed. double initial_lag=lag; while(lag>physics_dt){// you can use lag>0 if you want.  DoPhysics(dt);  virtual_time+=physics_dt;  real_time=CurrentTime();  double new_lag=real_time-virtual_time;  if(lag-new_lag<physics_dt*k){    the physics does not make lag much shorter.    You can do something about it right there,    or do it only if new_lag exceeds some treshold,     or increment counter and do something     if lag>initial_lag after too many steps.  }  lag=new_lag; } Render();}

BTW, don't use floating point for time. It has too bad precision, especially if game runs for long (may be especially important for server). I had this problem in one of my programs.

Another BTW, it may be good idea to make world run with negative lag and interpolate in the render. This way you could have physics framerate at something like 20fps and have rendering work at higher framerate. With good solver, 20fps or less is allright.
Quote:Original post by siaspete
Reading over your post again, it seems that the problem is that you can't guarantee that on average your do_physics takes less time than your timestep, which is an unwritten requirement for fixing the timestep.


That's not completely true. A low rendering framerate for a given frame will in turn increase the number of time steps to run in the next frame. Even if each time step is taking well under 10 ms to execute, the amount of them will cause the downwards spiral and it'll start to become worse and worse for the next frames (each time step staying with "okay-ish" performance).

Quote:Original post by siaspeteI can only think of two solutions:
1. Optimise do_physics to take less than a 100th of a second.


Won't solve the issue, as mentionned in the previous paragraph.

Quote:Original post by siaspete2. Reduce your update rate, to say 50/second instead of 100, depending on your preference.


Won't solve the issue, will simply make it less likely to happen. Take a computer that is twice slower, and you'll get it back.

Y.
Quote:Original post by Ysaneya
And you did not suffer from the problem i described ? When your framerate starts to slow down, it makes the number of physics steps explode..

My physics was very simple, so the number of physics steps per render was pretty stable at low frame rates. Something like 10 updates per draw. It was the draw which was slow, so that didn't matter.

Quote:Original post by Ysaneya
I've already read that article you read to. I find it interesting that he says you should understand all the "subtleties", calls his technique "perfect" and recommends it for "all game systems" ...

Yes, you're right there - it's clearly neither.
Some clarification: the idea of my method is that there's lag between virtual time (game time) and real time (the time in world where i live in).
The physics steps, essentially, is done to shorten the lag [after it has been increased by the rendering]. When physics is too slow and fails to shorten the lag or shorten the lag too little (and does this for few updates in the row) you really need to "do something" (e.g. switch physics to lower rate, or inform player that his hardware is too bad)

It is kind of like how automatic control systems (autopilots etc) works. Except for autopilots it is strictly verified that in no situation the updates may happen slower than real time.
Quote:Original post by Ysaneya
That's not completely true. A low rendering framerate for a given frame will in turn increase the number of time steps to run in the next frame. Even if each time step is taking well under 10 ms to execute, the amount of them will cause the downwards spiral and it'll start to become worse and worse for the next frames (each time step staying with "okay-ish" performance).


There simply cannot be a downward spiral into an infinite loop if your physics update is faster than your timestep.

Personally, I think a 10ms timestep is probably way too small given the problems you are experiencing.. have you considered a much bigger timestep (100ms) and interpolation between steps for rendering?

In any event.. you should always cap the number of physics steps per frame for those situations where an unexpected slowdown is unavoidable (user tabs out, background app hits the disk, etc) this also applies to variable timestep physics where you would cap dt to something reasonable.

I usually do like this :

  while(1){    ticks = SDL_GetTicks();    process_events();    for(i = 10; i--;) physics_simul();    draw();    delay = SDL_GetTicks() - ticks;    if((delay >= 0) && (delay < 20)){      SDL_Delay(20 - delay);  // 20 ms -> 50 fps    }  }


This way, if physics_simul() takes less than 2 ms, you get a constant framerate of 50Hz, and you can use a fixed timestep (of 2ms in this case) for the physics.

This topic is closed to new replies.

Advertisement