
# Objective Removal events are unwieldy and require some knowledge of when to put systems that need to catch events for them, it is very easy to end up missing one and end up with memory leak-ish issues where you don't clean up after yourself. ## Solution Consolidate removals with the benefits of `Events<...>` (such as double buffering and per system ticks for reading the events) and reduce the special casing of it, ideally I was hoping to move the removals to a `Resource` in the world, but that seems a bit more rough to implement/maintain because of double mutable borrowing issues. This doesn't go the full length of change detection esque removal detection a la https://github.com/bevyengine/rfcs/pull/44. Just tries to make the current workflow a bit more user friendly so detecting removals isn't such a scheduling nightmare. --- ## Changelog - RemovedComponents<T> is now backed by an `Events<Entity>` for the benefits of double buffering. ## Migration Guide - Add a `mut` for `removed: RemovedComponents<T>` since we are now modifying an event reader internally. - Iterating over removed components now requires `&mut removed_components` or `removed_components.iter()` instead of `&removed_components`.
63 lines
2.3 KiB
Rust
63 lines
2.3 KiB
Rust
//! This example shows how you can know when a `Component` has been removed, so you can react to it.
|
|
|
|
use bevy::prelude::*;
|
|
|
|
fn main() {
|
|
// Information regarding removed `Component`s is discarded at the end of each frame, so you need
|
|
// to react to the removal before the frame is over.
|
|
//
|
|
// Also, `Components` are removed via a `Command`. `Command`s are applied after a stage has
|
|
// finished executing. So you need to react to the removal at some stage after the
|
|
// `Component` is removed.
|
|
//
|
|
// With these constraints in mind we make sure to place the system that removes a `Component` on
|
|
// the `CoreStage::Update' stage, and the system that reacts on the removal on the
|
|
// `CoreStage::PostUpdate` stage.
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_startup_system(setup)
|
|
.add_system_to_stage(CoreStage::Update, remove_component)
|
|
.add_system_to_stage(CoreStage::PostUpdate, react_on_removal)
|
|
.run();
|
|
}
|
|
|
|
// This `Struct` is just used for convenience in this example. This is the `Component` we'll be
|
|
// giving to the `Entity` so we have a `Component` to remove in `remove_component()`.
|
|
#[derive(Component)]
|
|
struct MyComponent;
|
|
|
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
commands.spawn(Camera2dBundle::default());
|
|
commands.spawn((
|
|
SpriteBundle {
|
|
texture: asset_server.load("branding/icon.png"),
|
|
..default()
|
|
},
|
|
// Add the `Component`.
|
|
MyComponent,
|
|
));
|
|
}
|
|
|
|
fn remove_component(
|
|
time: Res<Time>,
|
|
mut commands: Commands,
|
|
query: Query<Entity, With<MyComponent>>,
|
|
) {
|
|
// After two seconds have passed the `Component` is removed.
|
|
if time.elapsed_seconds() > 2.0 {
|
|
if let Some(entity) = query.iter().next() {
|
|
commands.entity(entity).remove::<MyComponent>();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn react_on_removal(mut removed: RemovedComponents<MyComponent>, mut query: Query<&mut Sprite>) {
|
|
// `RemovedComponents<T>::iter()` returns an iterator with the `Entity`s that had their
|
|
// `Component` `T` (in this case `MyComponent`) removed at some point earlier during the frame.
|
|
for entity in &mut removed {
|
|
if let Ok(mut sprite) = query.get_mut(entity) {
|
|
sprite.color.set_r(0.0);
|
|
}
|
|
}
|
|
}
|