bevy
bevy copied to clipboard
Improve the query api to allow for iterating over Children easier
What problem does this solve or what need does it fill?
Considering systems that require us to iterate over an entity's children, I would like to suggest a new API for doing the same task. Currently the API for querying children can be unintuitive and difficult to use when dealing with complex systems. For example, let's take a look at a toy example, where I want to build a system to get all the enchantments for all items inside a given treasure box, currently I would implement it as:
fn get_all_loot_enchantments_from_treasure_box(
treasures: Query<(&TreasureBox, &Children)>,
items: Query<(&Item, &Children)>,
enchantments: Query<&Enchantment>,
) {
for (treasure, treasure_box_children) in treasures.iter() {
for treasure_box_child in treasure_box_children.iter() {
if let Ok((item, items_children)) = items.get(*treasure_box_child) {
for items_child in items_children.iter() {
if let Ok(enchantment) = enchantments.get(*items_child) {
println!(
"Treasure {:?} Has Item {:?} Has enchantment: {:?}",
&treasure, &item, &enchantment
);
}
}
}
}
}
}
For a three dimensional loop, like the example above, I need to have a lot of if let constructs in order to get to what I'm looking for.
This is both difficult to use, and not performant, as it requires us to individually filter all children which are not of the correct component type. In a situation where most of the entity's children are not of the desired type, this can be very non performant. It would be a lot better to handle this complexity on the ECS backend side, which will enable any optimization in the future to be made uniformly across all situations requiring child queries, rather than on a per system basis.
What solution would you like?
I would like for Children to take in a WorldQuery type parameter indicating the type of component that needs to be retrieved. Rather than the children component containing references to the entities themselves, they should contain components, or any other WorldQuery type. This means that query arguments with Children types will be replaced with the corresponding list of components of the desired type. For example, the above example can be reimplemented as:
fn get_all_loot_enchantments_from_treasure_box(
treasures: Query<(&TreasureBox, Children<(&Item, Children<&Enchantment>)>)>,
) {
for (treasure, treasure_box_children) in treasures.iter() {
for (item, item_children) in treasure_box_children.iter() {
for enchantment in item_children.iter() {
println!(
"Treasure {:?} Has Item {:?} Has enchantment: {:?}",
&treasure, &item, &enchantment
);
}
}
}
}
It would be even better if we could implement query filters for children as well. This will make the template types for Query the same as Children.
What alternative(s) have you considered?
Alternative already mentioned, but as stated above, it is not ergonomic.
Additional context
This may be beyond the scope of this request, however, I believe that entities with no Children should also be passed into the system runner, rather than having to explicitly wrap the Children parameter inside Option.
A similar API can also be used for Parent component queries.
This is a special case of #3742 :) This is an interesting proposal for an API, but it needs a full RFC to flesh out the exact mechanics and edge cases.
That issue sound really exciting! I'm still not super familiar with ECS's internals, but I'm starting to learn it more. I will make an RFC once I figure out how it all works better.
@OriginHamster Since 0.8 you can use iter_many instead:
let enchantments: Vec<&Enchantment> = treasures
.iter()
.flat_map(|(_, children)| items.iter_many(children))
.flat_map(|(_, children)| enchantments.iter_many(children))
.collect();
What @simon-an suggests works for a fixed level iterator. Does #6185 address the other use cases, @OriginHamster?