Removal Detection

Relevant official examples: removal_detection.


Removal detection is special. This is because, unlike with change detection, the data does not exist in the ECS anymore (obviously), so Bevy cannot keep tracking metadata for it.

Nevertheless, being able to respond to removals is important for some applications, so Bevy offers a limited form of it.

Components

You can check for components that have been removed during the current frame. The data is cleared at the end of every frame update. Note that this makes this feature tricky to use, and requires you to use multiple stages.

When you remove a component (using Commands), the operation is applied at the end of the stage. The system that checks for the removal must run in a later stage during the same frame update. Otherwise, it will not detect the removal.

Use the RemovedComponents<T> special system parameter type, to get an iterator for the Entity IDs of all the entities that had a component of type T that was removed earlier this frame.

/// Some component type for the sake of this example.
struct Seen;

fn main() {
    App::build()
        .add_plugins(DefaultPlugins)
        // we could add our system to Bevy's `PreUpdate` stage
        // (alternatively, you could create your own stage)
        .add_system_to_stage(CoreStage::PreUpdate, remove_components.system())
        // our detection system runs in a later stage
        // (in this case: Bevy's default `Update` stage)
        .add_system(detect_removals.system())
        .run();
}

fn remove_components(
    mut commands: Commands,
    q: Query<(Entity, &Transform), With<Seen>>,
) {
    for (e, transform) in q.iter() {
        if transform.translation.y < -10.0 {
            // remove the `Seen` component from the entity
            commands.entity(e)
                .remove::<Seen>();
        }
    }
}

fn detect_removals(
    removals: RemovedComponents<Seen>,
    // ... (maybe Commands or a Query ?) ...
) {
    for entity in removals.iter() {
        // do something with the entity
    }
}

(To do things with these entities, you can just use the Entity IDs with Commands::entity() or Query::get().)

Resources

Bevy does not provide any API for detecting when resources are removed.

You can work around this using Option and a separate Local system parameter, effectively implementing your own detection.

fn detect_removed_res(
    my_res: Option<Res<MyResource>>,
    mut my_res_existed: Local<bool>,
) {
    if let Some(my_res) = my_res {
        // the resource exists!

        // remember that!
        *my_res_existed = true;

        // (... you can do something with the resource here if you want ...)
    } else if *my_res_existed {
        // the resource does not exist, but we remember it existed!
        // (it was removed)

        // forget about it!
        *my_res_existed = false;

        // ... do something now that it is gone ...
    }
}

Note that, since this detection is local to your system, it does not have to happen during the same frame update.