Module camera_modification_in_fixed_update

Source
Expand description

Checks for systems added to the FixedUpdate schedule that mutably query entities with a Camera component.

§Motivation

Modifying the camera in FixedUpdate can cause jittery, inconsistent, or laggy visuals because FixedUpdate may not run every render frame, especially on games with a high FPS.

§Known Issues

This lint only detects systems that explicitly use the With<Camera> query filter.

§Example

fn move_camera(mut query: Query<&mut Transform, With<Camera>>) {
    // ...
}

fn main() {
    App::new()
        // Uh oh! This could cause issues because the camera may not move every frame!
        .add_systems(FixedUpdate, move_camera);
}

Use instead:

fn move_camera(mut query: Query<&mut Transform, With<Camera>>) {
    // ...
}

fn main() {
    App::new()
        // Much better. This will run every frame.
        .add_systems(Update, move_camera);
}

Any system that modifies the camera in a user-visible way should be run every render frame. The Update schedule is a good choice for this, but it notably runs after FixedUpdate. You can use the RunFixedMainLoop schedule with the RunFixedMainLoopSystem::BeforeFixedMainLoop system set to run a system before FixedUpdate:

fn rotate_camera(mut query: Query<&mut Transform, With<Camera>>) {
    // ...
}

fn main() {
    App::new()
        // In 3D games it is common for the player to move in the direction of the camera.
        // Because of this, we must rotate the camera before running the physics logic in
        // `FixedUpdate`. This will still run every render frame, though, so there won't be any
        // lag!
        .add_systems(
            RunFixedMainLoop,
            rotate_camera.in_set(RunFixedMainLoopSystem::BeforeFixedMainLoop),
        );
}

For more information, check out the physics in fixed timestep example.