Skip to content

Commit 13da481

Browse files
committed
Add methods to Query<&Children> and Query<&Parent> to iterate over descendants and ancestors (#6185)
# Objective Add methods to `Query<&Children>` and `Query<&Parent>` to iterate over descendants and ancestors, respectively. ## Changelog * Added extension trait for `Query` in `bevy_hierarchy`, `HierarchyQueryExt` * Added method `iter_descendants` to `Query<&Children>` via `HierarchyQueryExt` for iterating over the descendants of an entity. * Added method `iter_ancestors` to `Query<&Parent>` via `HierarchyQueryExt` for iterating over the ancestors of an entity. Co-authored-by: devil-ira <justthecooldude@gmail.com>
1 parent 8b9aa2c commit 13da481

File tree

5 files changed

+223
-12
lines changed

5 files changed

+223
-12
lines changed

crates/bevy_hierarchy/src/components/children.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ use core::slice;
1010
use smallvec::SmallVec;
1111
use std::ops::Deref;
1212

13-
/// Contains references to the child entities of this entity
13+
/// Contains references to the child entities of this entity.
14+
///
15+
/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`].
16+
///
17+
/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt
18+
/// [`Query`]: bevy_ecs::system::Query
1419
#[derive(Component, Debug, Reflect)]
1520
#[reflect(Component, MapEntities)]
1621
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);

crates/bevy_hierarchy/src/components/parent.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ use std::ops::Deref;
99

1010
/// Holds a reference to the parent entity of this entity.
1111
/// This component should only be present on entities that actually have a parent entity.
12+
///
13+
/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`].
14+
///
15+
/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt
16+
/// [`Query`]: bevy_ecs::system::Query
1217
#[derive(Component, Debug, Eq, PartialEq, Reflect)]
1318
#[reflect(Component, MapEntities, PartialEq)]
1419
pub struct Parent(pub(crate) Entity);

crates/bevy_hierarchy/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ pub use events::*;
1919
mod valid_parent_check_plugin;
2020
pub use valid_parent_check_plugin::*;
2121

22+
mod query_extension;
23+
pub use query_extension::*;
24+
2225
#[doc(hidden)]
2326
pub mod prelude {
2427
#[doc(hidden)]
2528
pub use crate::{
26-
child_builder::*, components::*, hierarchy::*, HierarchyPlugin, ValidParentCheckPlugin,
29+
child_builder::*, components::*, hierarchy::*, query_extension::*, HierarchyPlugin,
30+
ValidParentCheckPlugin,
2731
};
2832
}
2933

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
use std::collections::VecDeque;
2+
3+
use bevy_ecs::{
4+
entity::Entity,
5+
query::{ReadOnlyWorldQuery, WorldQuery, WorldQueryGats},
6+
system::Query,
7+
};
8+
9+
use crate::{Children, Parent};
10+
11+
/// An extension trait for [`Query`] that adds hierarchy related methods.
12+
pub trait HierarchyQueryExt<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> {
13+
/// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s descendants.
14+
///
15+
/// Can only be called on a [`Query`] of [`Children`] (i.e. `Query<&Children>`).
16+
///
17+
/// Traverses the hierarchy breadth-first.
18+
///
19+
/// # Examples
20+
/// ```
21+
/// # use bevy_ecs::prelude::*;
22+
/// # use bevy_hierarchy::prelude::*;
23+
/// # #[derive(Component)]
24+
/// # struct Marker;
25+
/// fn system(query: Query<Entity, With<Marker>>, children_query: Query<&Children>) {
26+
/// let entity = query.single();
27+
/// for descendant in children_query.iter_descendants(entity) {
28+
/// // Do something!
29+
/// }
30+
/// }
31+
/// # bevy_ecs::system::assert_is_system(system);
32+
/// ```
33+
fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, Q, F>
34+
where
35+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Children>;
36+
37+
/// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s ancestors.
38+
///
39+
/// Can only be called on a [`Query`] of [`Parent`] (i.e. `Query<&Parent>`).
40+
///
41+
/// # Examples
42+
/// ```
43+
/// # use bevy_ecs::prelude::*;
44+
/// # use bevy_hierarchy::prelude::*;
45+
/// # #[derive(Component)]
46+
/// # struct Marker;
47+
/// fn system(query: Query<Entity, With<Marker>>, parent_query: Query<&Parent>) {
48+
/// let entity = query.single();
49+
/// for ancestor in parent_query.iter_ancestors(entity) {
50+
/// // Do something!
51+
/// }
52+
/// }
53+
/// # bevy_ecs::system::assert_is_system(system);
54+
/// ```
55+
fn iter_ancestors(&'w self, entity: Entity) -> AncestorIter<'w, 's, Q, F>
56+
where
57+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Parent>;
58+
}
59+
60+
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> HierarchyQueryExt<'w, 's, Q, F>
61+
for Query<'w, 's, Q, F>
62+
{
63+
fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, Q, F>
64+
where
65+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Children>,
66+
{
67+
DescendantIter::new(self, entity)
68+
}
69+
70+
fn iter_ancestors(&'w self, entity: Entity) -> AncestorIter<'w, 's, Q, F>
71+
where
72+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Parent>,
73+
{
74+
AncestorIter::new(self, entity)
75+
}
76+
}
77+
78+
/// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`].
79+
///
80+
/// Traverses the hierarchy breadth-first.
81+
pub struct DescendantIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery>
82+
where
83+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Children>,
84+
{
85+
children_query: &'w Query<'w, 's, Q, F>,
86+
vecdeque: VecDeque<Entity>,
87+
}
88+
89+
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> DescendantIter<'w, 's, Q, F>
90+
where
91+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Children>,
92+
{
93+
/// Returns a new [`DescendantIter`].
94+
pub fn new(children_query: &'w Query<'w, 's, Q, F>, entity: Entity) -> Self {
95+
DescendantIter {
96+
children_query,
97+
vecdeque: children_query
98+
.get(entity)
99+
.into_iter()
100+
.flatten()
101+
.copied()
102+
.collect(),
103+
}
104+
}
105+
}
106+
107+
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for DescendantIter<'w, 's, Q, F>
108+
where
109+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Children>,
110+
{
111+
type Item = Entity;
112+
113+
fn next(&mut self) -> Option<Self::Item> {
114+
let entity = self.vecdeque.pop_front()?;
115+
116+
if let Ok(children) = self.children_query.get(entity) {
117+
self.vecdeque.extend(children);
118+
}
119+
120+
Some(entity)
121+
}
122+
}
123+
124+
/// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`].
125+
pub struct AncestorIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery>
126+
where
127+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Parent>,
128+
{
129+
parent_query: &'w Query<'w, 's, Q, F>,
130+
next: Option<Entity>,
131+
}
132+
133+
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> AncestorIter<'w, 's, Q, F>
134+
where
135+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Parent>,
136+
{
137+
/// Returns a new [`AncestorIter`].
138+
pub fn new(parent_query: &'w Query<'w, 's, Q, F>, entity: Entity) -> Self {
139+
AncestorIter {
140+
parent_query,
141+
next: Some(entity),
142+
}
143+
}
144+
}
145+
146+
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for AncestorIter<'w, 's, Q, F>
147+
where
148+
Q::ReadOnly: WorldQueryGats<'w, Item = &'w Parent>,
149+
{
150+
type Item = Entity;
151+
152+
fn next(&mut self) -> Option<Self::Item> {
153+
self.next = self.parent_query.get(self.next?).ok().map(|p| p.get());
154+
self.next
155+
}
156+
}
157+
158+
#[cfg(test)]
159+
mod tests {
160+
use bevy_ecs::{
161+
prelude::Component,
162+
system::{Query, SystemState},
163+
world::World,
164+
};
165+
166+
use crate::{query_extension::HierarchyQueryExt, BuildWorldChildren, Children, Parent};
167+
168+
#[derive(Component, PartialEq, Debug)]
169+
struct A(usize);
170+
171+
#[test]
172+
fn descendant_iter() {
173+
let world = &mut World::new();
174+
175+
let [a, b, c, d] = std::array::from_fn(|i| world.spawn(A(i)).id());
176+
177+
world.entity_mut(a).push_children(&[b, c]);
178+
world.entity_mut(c).push_children(&[d]);
179+
180+
let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world);
181+
let (children_query, a_query) = system_state.get(world);
182+
183+
let result: Vec<_> = a_query
184+
.iter_many(children_query.iter_descendants(a))
185+
.collect();
186+
187+
assert_eq!([&A(1), &A(2), &A(3)], result.as_slice());
188+
}
189+
190+
#[test]
191+
fn ancestor_iter() {
192+
let world = &mut World::new();
193+
194+
let [a, b, c] = std::array::from_fn(|i| world.spawn(A(i)).id());
195+
196+
world.entity_mut(a).push_children(&[b]);
197+
world.entity_mut(b).push_children(&[c]);
198+
199+
let mut system_state = SystemState::<(Query<&Parent>, Query<&A>)>::new(world);
200+
let (parent_query, a_query) = system_state.get(world);
201+
202+
let result: Vec<_> = a_query.iter_many(parent_query.iter_ancestors(c)).collect();
203+
204+
assert_eq!([&A(1), &A(0)], result.as_slice());
205+
}
206+
}

examples/3d/update_gltf_scene.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn move_scene_entities(
5151
) {
5252
for moved_scene_entity in &moved_scene {
5353
let mut offset = 0.;
54-
iter_hierarchy(moved_scene_entity, &children, &mut |entity| {
54+
for entity in children.iter_descendants(moved_scene_entity) {
5555
if let Ok(mut transform) = transforms.get_mut(entity) {
5656
transform.translation = Vec3::new(
5757
offset * time.elapsed_seconds().sin() / 20.,
@@ -60,15 +60,6 @@ fn move_scene_entities(
6060
);
6161
offset += 1.0;
6262
}
63-
});
64-
}
65-
}
66-
67-
fn iter_hierarchy(entity: Entity, children_query: &Query<&Children>, f: &mut impl FnMut(Entity)) {
68-
(f)(entity);
69-
if let Ok(children) = children_query.get(entity) {
70-
for child in children.iter().copied() {
71-
iter_hierarchy(child, children_query, f);
7263
}
7364
}
7465
}

0 commit comments

Comments
 (0)