Stepping The World
From Physics Simulation Wiki
What do the parameters to btDynamicsWorld::stepSimulation mean?
Here's the prototype:
btDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.));
The first parameter is the easy one. It's simply the amount of time to step the simulation by. Typically you're going to be passing it the time since you last called it
Bullet maintains an internal clock, in order to keep the actual length of ticks constant. This is pivotally important for framerate independence. The third parameter is the size of that internal step.
The second parameter is the maximum number of steps that Bullet is allowed to take each time you call it. If you pass a very large timeStep as the first parameter [say, five times the size of the fixed internal time step], then you must increase the number of maxSubSteps to compensate for this, otherwise your simulation is “losing” time.
How do I use this?
It's important that timeStep is always less than maxSubSteps*fixedTimeStep, otherwise you are losing time. Mathematically,
timeStep < maxSubSteps * fixedTimeStep
When you are calculating what figures you need for your game, start by picking the maximum and minimum framerates you expect to see. For example, I cap my game framerate at 120fps, I guess it might conceivably go as low as 12fps.
At 120fps, the timeStep I'm passing will be roughly 1/120th of a second, or 0.0083. The default fixedTimeStep is 1/60th of a second, or 0.017. In order to meed the equation above, timestep doesn't need to be greater than 1. At 120fps, with 1/60th of a second a tick, you're looking at interpolating [as opposed to more accurately simulating] one in every two ticks.
At 12fps, the timeStep will be roughly 1/12th of a second, or 0.083. In order to meet the equation above, maxSubSteps would need to be at least 5. Every time the game spikes a little and the framerate drops lower, I'm still losing time. So run with 6 or 7. At 12fps, with 1/60th of a second per tick, you're going to be getting maybe five genuine simulation steps every tick.
My call to btDynamicsWorld::stepSimulation here would therefore be
Any other important things to know?
The first and third parameters to stepSimulation are measured in seconds, and not milliseconds. A common and easy mistake is to just pass it the value returned by your system's getTime-equivalent function, which commonly returns time in milliseconds.
This mistake can give strange results such as: No framerate dependence no matter what you do. Objects not moving at all until you apply a huge force and then they give huge acceleration.
Simply divide the time by 1000.0f before passing it to stepSimulation.
By decreasing the size of fixedTimeStep, you are increasing the “resolution” of the simulation.
If you are finding that your objects are moving very fast and escaping from your walls instead of colliding with them, then one way to help fix this problem is by decreasing fixedTimeStep. If you do this, then you will need to increase maxSubSteps to ensure the equation listed above is still satisfied.
The issue with this is that each internal “tick” takes an amount of computation. More of them means your CPU will be spending more time on physics and therefore less time on other stuff. Say you want twice the resolution, you'll need twice the maxSubSteps, which could chew up twice as much CPU for the same amount of simulation time.
maxSubSteps == 0 ?
If you pass maxSubSteps=0 to the function, then it will assume a variable tick rate. Every tick, it will move the simulation along by exactly the timeStep you pass, in a single tick, instead of a number of ticks equal to fixedTimeStep.
Passing in a variable timestep in combination with 0 as second argument is not officially supported, and the death of determinism and framerate independence. Don't do it.
Bullet interpolates stuff, aka, maxSubSteps == 1 ?
When you pass Bullet maxSubSteps > 1, it will interpolate movement for you. This means that if your fixedTimeStep is 3 units, and you pass a timeStep of 4, then it will do exactly one tick, and estimate the remaining movement by 1/3. This saves you having to do interpolation yourself, but keep in mind that maxSubSteps needs to be greater than 1.
See also Simulation Tick Callbacks