Keep Your Framerate Out of My Physics - Part II

This is part II of a series looking at the physics algorithm for Electric Field Hockey. Part I outlines the initial algorithm and it’s shortcomings.

Last time we noticed a problem in how we generate a time step for our physics simulation: requestAnimationFrame can’t guarantee a consistent interval which makes the simulation appear frustratingly irrational.

Glenn Fiedler gives a solution in his article, Fix Your Timestep!,

All that is needed to solve this is to change your point of view. Instead of thinking that you have a certain amount of frame time you must simulate before rendering, flip your viewpoint upside down and think of it like this: The renderer produces time and the simulation consumes it in discrete [constant] sized chunks.

You should definitely read his article once or twice, and then we’ll apply it to the simulation at hand. In the following snippet, we rewrite the animation logic to maintain a constant simulation rate (simrate) of 60fps. No matter how quickly or how slowly the browser requests a new frame, we will only ever calculate the position 16.667ms later. By using an accumulator, we can store up the left over time. Every few frames we will actually perform multiple physics steps.

Simulation Tick Function - Take 2
1
2
3
4
5
6
7
8
9
10
11
12
var accumulator = 0.0;
var simrate = 1000/60; //60fps
self.anim = new Kinetic.Animation(function(frame) {
  accumulator += frame.timeDiff;
  while( accumulator >= simrate ) {
      self.tick({
          timeDiff: simrate,
          frameRate: frame.frameRate
      });
      accumulator -= simrate;
  }
}, self.layer);

The accumulator is important. We are still relying on requestAnimationFrame to trigger a redraw and subsequent recalculation of the simulation state. Imagine it asks for a frame every 20ms, but we are stepping the physics at 16ms per step. What about that extra 4ms? The accumulator hangs on to it until eventually there is enough time collected to actually perform two physics steps for one new frame. This keeps the system from falling behind.

But we can still do a little better. Depending on the rate of time accumulation, the simulation could begin to appear choppy as it corrects itself. Next time we’ll look at some refactoring that will allow us to apply a smoothing algorithm.

Comments