bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.89k stars 3.55k forks source link

Allow a Query to iterate over all elements that are not the current element of an existing iteration #4684

Open Luminoth opened 2 years ago

Luminoth commented 2 years ago

What problem does this solve or what need does it fill?

In the case I'm looking at, I have a set of TeamMembers and there are times where a single TeamMember needs to consider all other TeamMembers. When the data in question is mutable, this creates aliasing issues and is currently difficult to resolve - I've personally been using Events to do it, but that comes with the cost of a frame delay and a separation of logic.

What solution would you like?

Something like this is sort of how I could imagine it working:

query: Query<(Entity, &mut Transform), With<TeamMember>>

for element in query.iter_mut() {
    let mut best_support = None;
    let mut closest = f32::MAX;

    // iter_except() returns an iterator over all elements *except* the one given to it
    for mate in query.iter_except(element.0) {
        // if mate is closest (this reads the mate Transform),
        // save it as best_support and update closest
    }

    // move our translation closer to the best support
}

What alternative(s) have you considered?

It's possible that this could be resolved with ParamSet, but I'm not certain if it would work given that both queries would be querying the same data. As noted, I've solved it currently using Events, but the frame delay and logic separation that comes with that doesn't feel very good.

james7132 commented 2 years ago

Does Query::iter_combinations(_mut) not work here? Seems like it would do exactly what you want.

Luminoth commented 2 years ago

By my understanding, I don't believe so. iter_combinations gives you all of the 1:1 combinations but what I want is every 1:(everything else) combination. I could be wrong tho, but every time I try to write what it would look like with iter_combinations it falls apart.

Luminoth commented 2 years ago

I suppose for soundness reasons, my example probably has to look more like this:

query: Query<(Entity, &mut Transform), With<TeamMember>>

for (mut element, others) in query.this_new_iter_mut() {
    let mut best_support = None;
    let mut closest = f32::MAX;

    for mate in others {
        // if mate is closest (this reads the mate Transform),
        // save it as best_support and update closest
    }

    // move our translation closer to the best support
}

that way the "others" iterator isn't having to trust what's given to it.

I tried looking for something similar to this in itertools and didn't see anything.