rustgame-developmentbevy

How to iterate over Bevy Query while iterating over same or similar Query?


I'm working with Bevy ECS and trying to iterate over entities in a system, but I'm encountering issues with iterating inside an iterator (for example, iterating over entities while inside another iteration).

Here's my current approach:

pub fn move_particles(
    time: Res<Time>,
    mut particles1: Query<(Entity, &mut Particle, &mut Transform)>,
    mut particles2: Query<(Entity, &mut Particle, &mut Transform), Without<Particle>>,
) {
    
    let bounds = Vec2::new(WINDOW_SIZE.x, WINDOW_SIZE.y);
    for (e, mut particle1, mut transform1) in particles1.iter_mut() {
        particle1.update();
        transform1.translation += particle1.velocity * time.delta_secs();

        if transform1.translation.x < -bounds.x || transform1.translation.x > bounds.x {
            particle1.velocity.x *= -1.0;
        }
        if transform1.translation.y < -bounds.y || transform1.translation.y > bounds.y {
            particle1.velocity.y *= -1.0;
        }
    }

    for (e1, mut particle1, mut transform1) in particles1.iter_mut() {
        for (e2, mut particle2, mut transform2) in particles2.iter_mut() {
            if PartialEq::eq(&e1, &e2){
                continue;
            }
            let distance = transform1.translation.distance(transform2.translation);
            let influence_radius = f32::min(particle1.influence_radius, particle2.influence_radius);

            if distance < influence_radius && distance > 1.0 {
                let direction = (transform2.translation - transform1.translation).normalize();
                let force = Particle::calculate_magnetic_force(particle1.color, particle2.color);
                let attraction = direction * force * time.delta_secs();

                particle1.velocity += attraction;
                particle2.velocity -= attraction;
            }
        }
    }
}

However, I'm running into an issue where Bevy is throwing errors related to borrowing conflicts:

Cannot borrow 'particles' as mutable more than once at a time

How can I fix this or properly iterate over entities inside another iteration in Bevy ECS? Should I use ParamSet, Without, or some other method to avoid conflicts when modifying entities inside multiple nested iterations?


Solution

  • You can use the provided method iter_combinations_mut instead of manually creating them:

    pub fn move_particles(
        time: Res<Time>,
        mut particles: Query<(Entity, &mut Particle, &mut Transform)>,
    ) {
        let bounds = Vec2::new(WINDOW_SIZE.x, WINDOW_SIZE.y);
        for (e, mut particle1, mut transform1) in particles.iter_mut() {
            particle1.update();
            transform1.translation += particle1.velocity * time.delta_secs();
    
            if transform1.translation.x < -bounds.x || transform1.translation.x > bounds.x {
                particle1.velocity.x *= -1.0;
            }
            if transform1.translation.y < -bounds.y || transform1.translation.y > bounds.y {
                particle1.velocity.y *= -1.0;
            }
        }
    
        for [
            (e1, mut particle1, mut transform1),
            (e2, mut particle2, mut transform2),
        ] in particles.iter_combinations_mut()
        {
            let distance = transform1.translation.distance(transform2.translation);
            let influence_radius = f32::min(particle1.influence_radius, particle2.influence_radius);
    
            if distance < influence_radius && distance > 1.0 {
                let direction = (transform2.translation - transform1.translation).normalize();
                let force = Particle::calculate_magnetic_force(particle1.color, particle2.color);
                let attraction = direction * force * time.delta_secs();
    
                particle1.velocity += attraction;
                particle2.velocity -= attraction;
            }
        }
    }