Fixed Timestep

Relevant official examples: fixed_timestep.


If you need something to happen at fixed time intervals (a common use case is Physics updates), you can use Bevy's FixedTimestep Run Criteria.

use bevy::core::FixedTimestep;

// The timestep says how many times to run the SystemSet every second
// For TIMESTEP_1, it's once every second
// For TIMESTEP_2, it's twice every second

const TIMESTEP_1_PER_SECOND: f64 = 60.0 / 60.0;
const TIMESTEP_2_PER_SECOND: f64 = 30.0 / 60.0;

fn main() {
    App::build()
        .add_plugins(DefaultPlugins)
        .add_system_set(
            SystemSet::new()
                // This prints out "hello world" once every second
                .with_run_criteria(FixedTimestep::step(TIMESTEP_1_PER_SECOND))
                .with_system(slow_timestep.system())
        )
        .add_system_set(
            SystemSet::new()
                // This prints out "goodbye world" twice every second
                .with_run_criteria(FixedTimestep::step(TIMESTEP_2_PER_SECOND))
                .with_system(fast_timestep.system())
        )
        .run();
}

fn slow_timestep() {
    println!("hello world");
}

fn fast_timestep() {
    println!("goodbye world");
}

(thanks @billyb2 for contributing this example)

State

You can check the current state of the fixed timestep trackers, by accessing the FixedTimesteps resource. This lets you know how much time remains until the next time it triggers, or how much it has overstepped. You need to label your fixed timesteps.

See the [official example], which illustrates this.

Caveats

Note that, as this feature is implemented using Run Criteria, the systems are still called as part of the regular frame-update cycle, along with all of the normal systems. So, the timing is not exact.

The FixedTimestep run criteria simply checks how much time passed since the last time your systems were ran, and decides whether to run them during the current frame, or not, or run them multiple times, as needed.

Danger! Lost events!

By default, Bevy's events are not reliable! They only persist for 2 frames, after which they are lost. If your fixed-timestep systems receive events, beware that you may miss some events if the framerate is higher than 2x the fixed timestep.

One way around that is to use events with manual clearing. This gives you control over how long events persist, but can also leak / waste memory if you forget to clear them.