4

我正在和 Bevy 一起制作一个小机器人玩具,每个机器人的速度/加速度取决于它周围的机器人的位置和速度值。这意味着对于每个 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
        }
    }
}

但是,我不确定如何使用 Bevy 中的查询来执行此操作。假设我有一个系统move_boids

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

我收到类似这样的错误,因为我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

我不能对同一个查询进行嵌套迭代,所以我不确定获取每个 boid 周围 boid 信息的最佳方法。我应该将每个 boid 的位置和速度信息从第一个查询复制到 aHashMap<Entity, BoidData>中,然后在其中进行查找吗?还有什么更惯用的我可以做的吗?

4

1 回答 1

1

所以我想出了一个非常不雅的答案,我不确定它是否适用于 boids。由于在同一范围内有两个可变引用,您尝试执行的操作将不起作用。

我能够获得其他 boid,并使用Vec<u128>他们的 id 比较它们。我不会使用哈希图,因为我听说他们的查找速度较慢,但​​我没有尝试过。

我假设在我的示例中存在恒定数量的 boid,但您可以只使用 boid 向量的长度。我还为我的示例创建了一个随机的 boid 结构并使用了 rand。

const NUM_BOIDS: u32 = 10;
struct Boids(Vec<u128>);
struct Boid {
    position: f32,
    velocity: f32,
}
fn main() {
    App::build()
        ...
        .add_resource(Boids(Vec::new()))
        ...
}

fn setup(mut commands: Commands, mut boids: ResMut<Boids>) {
    let mut rng = rand::thread_rng();
    for i in 0..10 {
        // create 10 boids, and add there id's to the boids vec
        if let Some(entity) = commands
            .spawn((Boid {
                position: rng.gen_range(0.0, 100.0),
                velocity: rng.gen_range(0.0, 25.0),
            },))
            .current_entity()
        {
            boids.0.push(entity.id());
        };
    }
}

fn get_boids(mut boids: ResMut<Boids>, mut query: Query<(Entity, &mut Boid)>) {
    // go through all the boid ids
    for (i, boid_id) in boids.0.iter().enumerate() {
        // go through a second time
        for j in 0..NUM_BOIDS {
            // this gets the next boid id unless its at the end
            let other_boid_id = match boids.0.get(j as usize..(j as usize + 1)) {
                Some(e) => e,
                None => {
                    continue;
                }
            };
            // skip if it is the same boid
            if *boid_id == other_boid_id[0] {
                continue;
            }
            
            // since you can't have two mutable references at the same time
            // this gets the positions so those can be compared
            // and if the statement is true, a mutable reference can be gotten again.
            let mut first_position = 0.0;
            {
                let en = Entity::from_id(*boid_id);
                let first_boid: RefMut<Boid> = query.get_mut::<Boid>(en).unwrap();
                first_position = first_boid.position.clone();
            }
            let mut second_position = 0.0;
            {
                let en2 = Entity::from_id(other_boid_id[0]);
                let second_boid: RefMut<Boid> = query.get_mut::<Boid>(en2).unwrap();
                second_position = second_boid.position.clone();
            }
            if first_position - second_position < 0.001 {
                let en = Entity::from_id(*boid_id);
                let mut first_boid: RefMut<Boid> = query.get_mut::<Boid>(en).unwrap();
                first_boid.velocity = 15.0;
            }
        }
    }
}
于 2020-09-09T22:37:05.367 回答