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]。