如何在Bevy系统中进行嵌套查询?

pet*_*ter 8 bevy

我正在与 Bevy 一起制作一个小boids玩具,每个 boids 的速度/加速度取决于它周围 boids 的位置和速度值。这意味着对于每个 boid,我想运行一些依赖于其他 boid 的某个子集的逻辑。

这看起来基本上可能是一个嵌套的 for 循环:

for boid in boids {
    for other_boid in boids {
        if boid.id == other_boid.id {
            continue;
        }
        
        if boid.position.distance_to(other_boid.position) < PERCEPTION_DISTANCE {
            // change boid's velocity / acceleration
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我不确定如何在 Bevy 中进行查询。假设我有一个系统move_boids:

fn move_boids(mut query: Query<&Boid>) {
    for boid in &mut query.iter() {
        // I can't iterate over *other* boids here
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到类似这样的错误,因为我query在两个循环中都可变地借用:

error[E0499]: cannot borrow `query` as mutable more than once at a time
  --> src\main.rs:10:32
   |
10 |     for boid in &mut query.iter() {
   |                      ------------
   |                      |          |
   |                      |          ... and the first borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `bevy::bevy_ecs::system::query::QueryBorrow`
   |                      first mutable borrow occurs here
   |                      a temporary with access to the first borrow is created here ...
...
11 |         for other_boid in &mut query.iter() {}
   |                                ^^^^^ second mutable borrow occurs here
Run Code Online (Sandbox Code Playgroud)

我无法对同一查询进行嵌套迭代,因此我不确定获取每个 boid 周围 boids 信息的最佳方法。我应该将每个主体的位置和速度信息从第一个查询复制到 a 中HashMap<Entity, BoidData>,然后在其中进行查找吗?我可以做一些更惯用的事情吗?

pet*_*ter 4

几年后回答我自己的问题。Bevy 0.6 版本引入了Query::iter_combinations和Query::iter_combinations_mut,后者正是您想要的。

因此,要使用问题中的示例系统,它看起来像这样:

fn move_boids(mut query: Query<&mut Boid>) {
    let mut combinations = query.iter_combinations_mut();
    while let Some([mut a, mut b]) = combinations.fetch_next() {
        // update velocity and acceleration
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,来自 Bevy 文档:

返回的值不是Iterator,因为这会导致可变引用的别名。为了迭代它,请使用fetch_next带有循环模式的方法while let Some(..)。

另请参阅Bevy 存储库中 iter_combinations 的官方示例。