佐藤のメモ帳

Rust, Python, Java, AWS, etc...

Rust Bevy Change Detectionについて

はじめに

本記事はBevy非公式(と思えないほど素晴らしい)サイトのメモである。

bevy-cheatbook.github.io

メモ

BevyにおいてChange Detectionとはデータの変更を検知する機能である。 データの変更を検知し、それに応じたアクションを定義できる。

データの変更検知では、以下2つのQuery Filterを使用する。

  • Added<T>

    • 指定した型のComponentの生成を検知する
      • Entityに指定したComponentが追加された場合や、指定したComponentを持つEntityが生成された場合、検知する
  • Changed<T>

    • 指定したComponentの変更を検知する
      • 指定したComponentの値が変更された場合や、指定したComponentが新たに追加された場合、検知する
/// PlayerのHealth, Xpの変更をChangedで受け取り、Health、Xpを出力する
fn debug_stats_change(
    query: Query<
        // components
        (&Health, &PlayerXp),
        // filters
        (Without<Enemy>, Or<(Changed<Health>, Changed<PlayerXp>)>), 
    >,
) {
    for (health, xp) in query.iter() {
        eprintln!(
            "hp: {}+{}, xp: {}",
            health.hp, health.extra, xp.0
        );
    }
}

/// Enemy追加をAddedで検知し、その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);
    }
}

Changedでの検知はDerefMutによってトリガーされる。
ミュータブルなQueryでComponentにアクセスするだけではトリガーされない。
変更後の値が変更前と同じであっても、トリガーは発動してしまうので、以下のように条件分けする必要がある。

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