Hello – my name is Cort, and I’m one of the engineers working on the Entities package at Unity.
One feature we’ve had many requests for in DOTS is the ability to update an ECS system with a constant, frame-rate-independent timestep, similar to the .FixedUpdate()
method on MonoBehaviours
. In addition to being a critical requirement for stable, deterministic physics simulation, this feature has many applications in gameplay code as well. I’m pleased to report that fixed timestep system updates will be available in an upcoming Entities package release and wanted to give a sneak peek at how it will be exposed and what its initial capabilities and limitations will be.
The groundwork for this feature was laid in the Entities 0.7.0 release, when we added the UpdateCallback
property to ComponentSystemGroup
objects. If this callback is not set (the default), system groups update as usual. If a callback is added, the system group updates its contents in a loop until the callback returns false.
In the same release, we added the FixedRateUtils
class, which contains some sample callbacks intended to be used with this feature. One of them, FixedRateUtils.FixedRateCatchUpManager
, essentially configures the system to run like FixedUpdate()
: when enabled, the system group temporarily overrides World.Time.ElapsedTime
and World.Time.DeltaTime
for the duration of its update, and then updates as many times as necessary to “catch up” to the actual elapsed time. The original ElapsedTime
and DeltaTime
values of World.Time
are restored just before the system group update ends.
In an upcoming release (tentatively Entities 0.12.0-preview), we’ll add a new system group that uses this catch-up feature by default. A new ComponentSystemGroup
will be added to default DOTS Worlds (tentatively called the FixedStepSimulationSystemGroup
). It will update at the beginning of the SimulationSystemGroup
(after the BeginSimulationEntityCommandBufferSystem
, but before any other systems in the group). It will use FixedRateCatchUpManager
, with a default timestep of 1/60th of a second. It will contain begin/end ECB systems so that systems in the group can instantiate/destroy entities and make other structural changes.
In a subsequent release of the DOTS physics packages, physics systems will be modified to update in the new FixedStepSimulationSystemGroup
instead of the SimulationSystemGroup
. This may require application changes: any user systems that currently order themselves relative to the physics systems will need to update their ordering constraints and/or their system groups.
DOTS Physics systems will refer to World.Time.DeltaTime
in their simulation code, instead of UnityEngine.Time.fixedDeltaTime
. This puts the timestep used by the physics system completely under the control of its parent system, and breaks the direct dependency on UnityEngine.Time
.
If you have any questions about the plan, let me know in the comments. Here’s a few FAQs:
Q: Can I override the default fixed timestep value?
A: Yes, the timestep is a property on the FixedStepSimulationSystemGroup
. Changing this property has the same effect for the group as changing UnityEngine.Time.fixedDeltaTime
does for MonoBehaviour
fixed updates: the next fixed-timestep group update will occur at LastUpdateTime + newTimestep.
Q: What systems will update in the fixed-timestep system group by default?
A: Currently, only the physics systems will move into the fixed-timestep system group. This will happen in a release of Unity Physics following the Entities release where the new group is available.
Notably, the TransformSystemGroup
(which is responsible for computing LocalToWorld
/LocalToParent
matrices and adding/removing Parent/Child relationships) will continue to update in the main SimulationSystemGroup
. Systems inside the fixed-timestep system group should not rely on these Transform components having been updated by the next fixed update because the next fixed update might be a catch-up update in the same frame!
For example, if inside the fixed-timestep system group, you add a Parent component to make entity B the parent of entity A, you should not expect entity B to have the Child component by the next fixed update. The Child component is added by the TransformSystemGroup
, which is only updated after the fixed-timestep system group has finished all of its updates for the current display frame.
Dynamic rigid bodies affected by physics should present no problems since they read from and write directly to Translation and Rotation components of root-level entities.
Q: Does the FixedStepSimulationSystemGroup
support anything like the RigidBodyInterpolation feature on RigidBody components, to provide smoother motion at low simulation tick rates by interpolating/extrapolating the transforms of simulated objects based on previous fixed-timestep results?
A: No. We do not support this feature. Systems running after the fixed-timestep system group (including the rest of the variable-timestep simulation group and the presentation group) will only see the results of the most recent fixed-timestep system group update. If the display frame rate is higher than the fixed-timestep simulation rate, the motion of simulated objects may appear jerky.
The simplest workaround for this issue is to keep the simulation rate and the display rate as close as possible, either by artificially capping the display rate, or manually decreasing the fixed timestep value.
In some cases, applications may want to disable the “catch-up” semantics of the fixed timestep group entirely, and have it update exactly once per display frame; this is an simple change to make during application bootstrap time. The fixed timestep system group can either continue to use a constant deltaTime, or it can fall back on the default, frame-rate-dependent deltaTime. Each approach has pros and cons for the application to consider:
Fixed Timestep with catch-up
PRO: Simulation determinism. Simulation stability. Matches familiar FixedUpdate()
behavior. Simulation time tracks “real” elapsed time.
CON: Jerky motion if display rate > simulation rate. Less predictable overall frame rate due to normal frame time fluctuation (even with a ~60fps display rate, the fixed timestep group will update zero times in some frames, twice in others). Running the simulation >1 times per frame can become expensive.
Fixed Timestep without catch-up
PRO: Simulation stability. Predictable frame times (exactly one simulation tick per frame). No jerkiness at high display frame rates.
CON: Simulation rate is locked to display rate; large changes in the display rate will cause the simulation to run correspondingly faster / slower. Simulation’s elapsed time will drift from “real” elapsed time. This approach is best when a stable frame rate can be guaranteed.
Variable Timestep (fixed timestep disabled)
PRO: Predictable frame times (exactly one simulation tick per frame). No jerkiness at high display rates. Simulation time tracks “real” elapsed time.
CON: Potential simulation instability (frame rate spikes → large deltaTime). Non-deterministic simulation.
Q: Can I read / process user input in fixed updates?
A: Yes, if proper care is taken to make sure that input events are applied to the correct simulation tick(s).
Polling an input device returns events that occurred during a particular window of “real” time (since the last poll occured). Without fixed timesteps, this is the same window of time the application is about to simulate, so input events can be consumed immediately. When fixed timesteps are enabled, however, the simulation window no longer necessarily corresponds with the input-polling window; without proper care, input events may be processed multiple times, or processed too early, or ignored entirely. Developers who have worked with input processing in MonoBehaviour.FixedUpdate()
may be very familiar with these challenges.
To avoid these problems, input events must be timestamped and bucketed by which simulation frame they should apply to. These events may need to be buffered across multiple display frames until the fixed-timestep system group processes the appropriate simulation tick.
We plan to release sample scenes in the Entities package that demonstrate correct input processing when fixed timesteps are enabled.