This section describes the internal timing of the simulation.
General
Time values should be specified as TimeSpans for optimal accuracy. Warning: Do not use the method TimeSpan..::..FromSeconds(Double) to convert a floating-point value to a TimeSpan. The method will round the time value to milliseconds. Fractions of milliseconds will be lost!
| ||||
Each call of Simulation..::..Update(TimeSpan) advances the simulation state by a given time step. Internally, the time step specified in the parameter of Update is divided into sub time steps (also called internal time steps) of constant size. This constant size is defined in TimingSettings..::..FixedTimeStep. That means, if the user calls
| C# | |
|---|---|
TimeSpan deltaTime = new TimeSpan(333333); // 1/30 s Simulation.Update(deltaTime) | |
and FixedTimeStep is 1/60 s (default), the simulation will internally perform two sub time steps with 1/60 s.
If the user calls
| C# | |
|---|---|
TimeSpan deltaTime = new TimeSpan(83333); // 1/120 s Simulation.Update(deltaTime) | |
the simulation will perform no simulation update because the time step is less than FixedTimeStep. But the time is not "lost". When the user calls
| C# | |
|---|---|
TimeSpan deltaTime = new TimeSpan(83333); // 1/120 s Simulation.Update(deltaTime) | |
the next time, the simulation will make a single time step with 1/60 s. - It is allowed to use variable time parameters for each Update call. The simulation class makes sure that all simulation updates use fixed time steps internally and that overall no simulation time is lost.
The simulation has a property Simulation..::..Time which contains the actual simulation time. This time is the sum of all sub time steps that have been simulated. The property Simulation..::..TargetTime contains the target time to which the simulation should advance. This time is the sum of all sub time steps plus any accumulated time.
The Influence of the Time Step Size
TimingSettings..::..FixedTimeStep defines the size of one sub time step. The default value is 1/60 seconds (60 Hz). Increasing the time step size makes the simulation faster, but less stable. Decreasing the time step size makes the simulation slower, more accurate and more stable (higher stacks of boxes are possible, less jittering, less interpenetrations of objects).
Limiting the Max Number Of Sub Time Steps
The number of time steps per Simulation..::..Update(TimeSpan) call is actually limited by TimingSettings..::..MaxNumberOfSteps. This is necessary to avoid a typical problem in game physics where too many objects cause the framerate to go to zero until the game freezes (spiral of death).
Here is an example: There are too many physical objects in the game. One frame of the game took 0.016 ms to compute. The simulation is called with Update(0.016) and it performs 1 time step internally. Because there are too many objects, this time step took longer and the frame took 0.034 ms to compute. In the next frame Update(0.034) is called and the simulation performs 2 time steps internally - which takes even more time... Each frame the simulation must make more internal time steps to keep up with the elapsed time and eventually the frame rate of the game will go towards 0. To avoid this scenario the maximal number of allowed sub time steps is limited. Practically this means that when this limit is reached, time is lost and the simulation moves objects in slow motion. Slow motion at a low frame rate is better than exact motion at 0 frames per second.
Sampling Simulation Results and the SubTimeStepFinished Event
In the simplest case the game loop (or the main loop of the application) and the simulation use the same fixed time steps. The game logic updates simulation objects, then Simulation..::..Update(TimeSpan) is called. Then the game loop reads the simulation results, updates the simulation objects and Simulation..::..Update(TimeSpan) is called again. And so forth.
If the time step of the game loop is larger than the fixed time step of the simulation, then the simulation will make several sub time steps in one Simulation..::..Update(TimeSpan) call. The game loop will not see the intermediate results of the sub time step. For example, the Simulation..::..CollisionDomain will display the final state of the simulation. Any intermediate collision are not visible.
In some cases this can be a problem: For example if the game loop wants to play sound effects for all collisions. Therefore, the simulation raises the Simulation..::..SubTimeStepFinished event after each internal sub time step. The game loop can handle this event, for example, to check the collision domain to see the intermediate simulation results.
Any event handler of the Simulation..::..SubTimeStepFinished event should be a light-weight method that does not manipulate the simulation objects. The method should only query the simulation results and cache them for later use after Simulation..::..Update(TimeSpan) has finished. This is recommended because in a modern game loop, the physics engine will be updated parallel to other game modules (like audio or graphics).
If the game loop is updated with smaller time step than the fixed time step of the simulation, then it can happen that the simulation does nothing in Simulation..::..Update(TimeSpan) and no Simulation..::..SubTimeStepFinished event is raised. (Simulation..::..Update(TimeSpan) will only accumulate the time until enough time for a fixed time step has elapsed.)
Running the Simulation with a Variable Time Step
Physics simulations should use a fixed time step. Using variable time steps is not recommended, and therefore DigitalRune Physics does not contain a direct option to switch to a variable time step mode. Nevertheless, it still possible to force the simulation to use a variable time step: Simply set TimingSettings..::..FixedTimeStep to the current elapsed time (must be > 0) before each Simulation..::..Update(TimeSpan) call.
Recommended Reading
For more in-depth information about physics simulations and time stepping, read the excellent article "Fix Your Timestep!", by Glenn Fiedler.