区分移除与消失

编程语言 2026-07-09

我有一个简单的Bevy 0.18观察者,它应该在对应的公开组件被移除时移除一个内部使用的组件:

fn on_remove_animated(event: On<Remove, Animated>, mut commands: Commands) {
    commands.entity(event.entity).remove::<TimedBy>();
}

但该观察者在整个实体被撤销时也会触发,在这种情况下,延迟删除命令会发出一个警告:

WARN bevy_ecs::error::handler: Encountered an error in command `<bevy_ecs::system::commands::entity_command::remove<engine::animation::TimedBy>::{{closure}} as bevy_ecs::error::command_handling::CommandWithEntity<core::result::Result<(), bevy_ecs::world::error::EntityMutableFetchError>>>::with_entity::{{closure}}`: Entity despawned: The entity with ID 677v0 is invalid; its index now has generation 1.
Note that interacting with a despawned entity is the most common cause of this error but there are others

    If you were attempting to apply a command to this entity,
    and want to handle this error gracefully, consider using `EntityCommands::queue_handled` or `queue_silenced`.

是否有办法区分是在仅删除了 Animated 组件,还是整个实体被撤销?如果不能,是否有其他方法,在移除 Animated 组件时安全地移除 TimedBy 组件? 或者我是否必须使用 queue_silenced() 来省略警告?如果这样做,在将尝试在已被撤销的实体上移除组件的命令排队时,是否会有副作用?

解决方案

根据 Remove 的文档,观察者是在组件仍然存在时触发的,这也意味着相关的实体也存在,因此我们无法查询实体是否正在被移除。

当一个组件从实体上被移除时触发,并在组件被移除前执行,因此你仍然可以访问组件数据

但还有另一种方式!通过快速浏览 撤销实体并调用观察者的代码,我发现Bevy会传递关于新实体原型的信息:

deferred_world.trigger_raw(
    REMOVE,
    &mut Remove {
        entity: self.entity,
    },
    &mut EntityComponentsTrigger {
        components: archetype.components(),
        old_archetype: Some(archetype),
        new_archetype: None,
    },
    caller,
);

如果一个实体被完全撤销,它会传递 None

在观察者中检查实体是否正在被撤销

:本答案自Bevy 0.19版本起才实际可用。

是否有办法区分是仅仅移除了 Animated 组件,还是整个实体被撤销?

要检查实体是否正在被撤销,我们可以查看它的新原型——可以通过 on_despawn.trigger().new_archetype 访问。

fn on_remove_animated(event: On<Remove, Animated>, mut commands: Commands) {
    if event.trigger().new_archetype.is_some() {
        commands.entity(event.entity).remove::<TimedBy>();
    }
}

简单地消除警告

:此代码在Bevy的早期版本也同样适用。

还是需要使用 queue_silenced() 来省略警告?

你可以使用那个,或者更简单的方式是使用 try_remove

fn on_remove_animated(event: On<Remove, Animated>, mut commands: Commands) {
    commands.entity(event.entity).try_remove::<TimedBy>();
}

我会说,在像删除相关组件这样的简单场景下,不必犹豫去抑制警告。在你的用例中,这种方法与前一种一样有效,因为正是引入 try_remove 的原因。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章