移除检测

可参考的官方例子: removal_detection.


移除检测很特别,这是因为,与变更检测不同,移除对象的数据不再存在于 ECS 中(很明显),所以 Bevy 无法保持跟踪它的元数据。

尽管如此,能够对移除作出反应对某些应用来说是很重要的,所以 Bevy 提供了一种有限的形式。

组件

你可以检查在当前帧中已经被移除的组件,该数据在每一帧更新结束时被清除。注意,这使得这个功能使用起来很麻烦,需要你使用多个阶段

当你删除一个组件时(使用命令),该操作会在阶段结束时生效,在同一帧更新期间,检查移除的系统必须在移除操作所在阶段的后一个阶段运行。否则,它将无法检测到移除的情况。

使用 RemovedComponents<T> 这个特殊的系统参数类型,可以得到一个迭代器,用来获取所有实体的 ID,这些实体有一个类型为 T 的组件,在这个帧的早的时候被移除。

    /// 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
        }
    }

(要对这些实体进行处理,你可以直接用 Commands::entity()Query::get() 来使用实体的 ID。)

资源

Bevy 没有提供任何 API 来检测资源何时被移除。

你可以使用 Option 和一个单独的 Local 系统参数来解决这个问题,有效地实现你自己的检测。

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 ...
    }
}

请注意,由于这个检测是在你的系统本地进行的,所以不要求它要在同一帧更新期间发生。