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
(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.
#[derive(Component)]
struct Seen;
fn main() {
App::new()
.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)
// our detection system runs in a later stage
// (in this case: Bevy's default `Update` stage)
.add_system(detect_removals)
.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.