Latest commit d38037d

变化检测

相关的官方文档:[change_detection]


Bevy 能够检测数据变化。可以在数据的变化时执行某些操作。ECS 中组件储存了数据,检测数据也就是检测组件。

todo:没用过,不懂 大意是其中一个用例是优化,仅在数据变化时执行相关操作,避免不必要的操作;另一个用例是配置内容或发送数据。

组件

过滤器

用 Query 来检测特定组件的变化情况。

使用 [Query Filters]:

  • Added<T>:检测新的组件实例

    • 如果把组件添加到实体中
    • 如果带有组件的实体被创建
  • Changed<T>:检测组件实例是否被修改

    • 组件被可变地访问
    • 组件刚被添加(Added

(如果想检测删除行为,请查看 [Removal Detection]。删除检测的工作方式不同,使用起来也比较麻烦。)

#![allow(unused)]
fn main() {
/// 当队友发生改变时打印队友的统计数据
fn debug_states_change(
    query: Query<
        // 组件
        (&Health, &PlayerXp),
        // 过滤器
        (Without<Enemy>, Or<(Changed<Health>, Changed<PlayerXp>)>),
    >,
) {
    for (health, xp) in query.iter() {
        eprintln!(
            "hp: {}+{}, xp: {}",
            health.hp, health.extra, xp.0
        );
    }
}

/// 检测新的敌人,并打印敌人的 Health
fn debug_new_hostiles(
    query: Query<(Entity, &Health), Added<Enemy>>,
) {
    for (entity, health) in query.iter() {
        eprintln!("Entity {:?} is now an enemy! HP: {}", entity, health.hp);
    }
}
}

检测

todo

资源

对于资源,可以通过 Res/ResMut 的方法来检测变化。

#![allow(unused)]
fn main() {
fn check_res_changed(
    my_res: Res<MyResource>,
) {
    if my_res.is_changed() {
        // 做一些事
    }
}

fn check_res_added(
    // 用到 Option,防止资源不存在时 panic!
    my_res: Option<Res<MyResource>>,
) {
    if let Some(my_res) = my_res {
        // 资源存在
        if my_res.is_added() {
            // 资源刚刚被添加,做一些事
        }
    }
}
}

注意,目前变化检测不能检测 state 的变化,是个 bug

检测到什么?

todo:不懂啊 changed 的检测由 DerefMut触发。仅仅通过可变的查询来访问组件,而不实际通过 &mut 来访问组件,就不会触发检测。这样能让变化检测非常准确。变化检测可以用来优化游戏性能,或者触发其他逻辑行为。

注意:可变地访问组件时,Bevy 不会跟踪新值是否与旧值不同,而是一直触发变化检测。如果要在值变化时触发检测,可以手动实现:

#![allow(unused)]
fn main() {
fn update_player_xp(
    mut query: Query<&mut PlayerXp>,
) {
    for mut xp in query.iter_mut() {
        let new_xp = maybe_lvl_up(&xp);

        // 只在数值不同时触发变化检测
        if new_xp != *xp {
            *xp = new_xp;
        }
    }
}
}

变化检测是可靠的,因为它会检测自上次检测系统运行以来发生的任何变化。 如果某个系统只是有时运行(用了 [States] 或 [Run Criteria]),不用担心会错过变化检测。

可能存在的陷阱

小心 [Avoiding Frame Delays / 1-frame-lag]。如果 Bevy 在运行“修改数据”的系统之前运行“检测数据变化”的系统,就会发生这种情况。,这时检测系统会在下一帧更新时得知数据被修改。如果要在同一帧处理“检测”和“修改”,可以用 [System Order of Execution]。但是,如果用 Added<T> 来检测组件的添加(通常是 Commands),还要使用 [Stages]。