I'm working on a little boids toy with Bevy, and each boid's velocity/acceleration depends on the position and velocity values of the boids around it. This means that for each boid, I want to run some logic that depends on some subset of the other boids.
This seems like it could be a nested for-loop basically:
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
}
}
}
However, I'm not sure how to do this with queries in Bevy. Let's say I have a system move_boids
:
fn move_boids(mut query: Query<&Boid>) {
for boid in &mut query.iter() {
// I can't iterate over *other* boids here
}
}
I get an error something like this, because I'm borrowing query
mutably in both loops:
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
I can't do nested iteration over the same Query, so I'm not sure the best way to get information about surrounding boids for each boid. Should I copy each boid's position and velocity information from the first query into a HashMap<Entity, BoidData>
and then do lookups in that? Is there something more idiomatic I could do?
Answering my own question a couple years later. The Bevy 0.6 release introduced Query::iter_combinations
and Query::iter_combinations_mut
, the latter of which is what you want here.
So, to use the example system from the question, it would look like this:
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
}
}
Note, from the Bevy docs:
The returned value is not an
Iterator
, because that would lead to aliasing of mutable references. In order to iterate it, usefetch_next
method withwhile let Some(..)
loop pattern.
See also the official example in the Bevy repo of iter_combinations.