1use core::marker::PhantomData;
2
3use bevy_ecs::{
4 component::{ComponentHooks, ComponentId, StorageType},
5 prelude::*,
6 world::{Command, DeferredWorld},
7};
8use bevy_hierarchy::BuildChildren;
9
10#[derive(Debug, Clone, Default)]
39pub struct ChildBundle<B: Bundle>(pub B);
40
41impl<B: Bundle> Component for ChildBundle<B> {
42 const STORAGE_TYPE: StorageType = StorageType::SparseSet;
44
45 fn register_component_hooks(hooks: &mut ComponentHooks) {
46 hooks.on_add(with_child_hook::<B>);
47 }
48}
49
50fn with_child_hook<B: Bundle>(
54 mut world: DeferredWorld<'_>,
55 entity: Entity,
56 _component_id: ComponentId,
57) {
58 world.commands().queue(ChildBundleCommand {
60 parent_entity: entity,
61 _phantom: PhantomData::<B>,
62 });
63}
64
65struct ChildBundleCommand<B> {
66 parent_entity: Entity,
67 _phantom: PhantomData<B>,
68}
69
70impl<B: Bundle> Command for ChildBundleCommand<B> {
71 fn apply(self, world: &mut World) {
72 let Ok(mut entity_mut) = world.get_entity_mut(self.parent_entity) else {
73 #[cfg(debug_assertions)]
74 panic!("Parent entity not found");
75
76 #[cfg(not(debug_assertions))]
77 return;
78 };
79
80 let Some(with_child_component) = entity_mut.take::<ChildBundle<B>>() else {
81 #[cfg(debug_assertions)]
82 panic!("ChildBundle component not found");
83
84 #[cfg(not(debug_assertions))]
85 return;
86 };
87
88 let child_entity = world.spawn(with_child_component.0).id();
89 world.entity_mut(self.parent_entity).add_child(child_entity);
90 }
91}
92
93#[derive(Debug, Clone, Default)]
142pub struct ChildBundles<B: Bundle, I: IntoIterator<Item = B>>(pub I);
143
144impl<B: Bundle, I: IntoIterator<Item = B> + Send + Sync + 'static> Component
145 for ChildBundles<B, I>
146{
147 const STORAGE_TYPE: StorageType = StorageType::SparseSet;
149
150 fn register_component_hooks(hooks: &mut ComponentHooks) {
151 hooks.on_add(with_children_hook::<B, I>);
152 }
153}
154
155fn with_children_hook<B: Bundle, I: IntoIterator<Item = B> + Send + Sync + 'static>(
159 mut world: DeferredWorld<'_>,
160 entity: Entity,
161 _component_id: ComponentId,
162) {
163 world.commands().queue(ChildBundlesCommand {
165 parent_entity: entity,
166 _phantom: PhantomData::<(B, I)>,
167 });
168}
169
170struct ChildBundlesCommand<B, I> {
171 parent_entity: Entity,
172 _phantom: PhantomData<(B, I)>,
173}
174
175impl<B: Bundle, I: IntoIterator<Item = B> + Send + Sync + 'static> Command
176 for ChildBundlesCommand<B, I>
177{
178 fn apply(self, world: &mut World) {
179 let Ok(mut entity_mut) = world.get_entity_mut(self.parent_entity) else {
180 #[cfg(debug_assertions)]
181 panic!("Parent entity not found");
182
183 #[cfg(not(debug_assertions))]
184 return;
185 };
186
187 let Some(with_children_component) = entity_mut.take::<ChildBundles<B, I>>() else {
188 #[cfg(debug_assertions)]
189 panic!("ChildBundle component not found");
190
191 #[cfg(not(debug_assertions))]
192 return;
193 };
194
195 for child_bundle in with_children_component.0 {
196 let child_entity = world.spawn(child_bundle).id();
197 world.entity_mut(self.parent_entity).add_child(child_entity);
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use bevy_ecs::system::RunSystemOnce;
205 use bevy_hierarchy::Children;
206
207 use super::*;
208
209 #[derive(Component, PartialEq, Debug)]
210 struct A;
211
212 #[derive(Component, PartialEq, Debug)]
213 struct B(u8);
214
215 #[derive(Bundle)]
216 struct ABBundle {
217 a: A,
218 b: B,
219 }
220
221 #[derive(Bundle)]
222 struct HierarchicalBundle {
223 a: A,
224 child: ChildBundle<ABBundle>,
225 }
226
227 #[test]
228 fn with_child() {
229 let mut world = World::default();
230
231 let parent = world.spawn(ChildBundle((A, B(3)))).id();
232 world.flush();
234
235 assert!(!world.entity(parent).contains::<ChildBundle<(A, B)>>());
236 assert!(!world.entity(parent).contains::<A>());
237 assert!(!world.entity(parent).contains::<B>());
238
239 let children = world.get::<Children>(parent).unwrap();
240 assert_eq!(children.len(), 1);
241
242 let child_entity = children[0];
243 assert_eq!(world.get::<A>(child_entity), Some(&A));
244 assert_eq!(world.get::<B>(child_entity), Some(&B(3)));
245 }
246
247 #[test]
248 fn with_children_vec() {
249 let mut world = World::default();
250
251 let parent = world.spawn(ChildBundles(vec![B(0), B(1), B(2)])).id();
252 world.flush();
254
255 assert!(!world.entity(parent).contains::<ChildBundles<B, Vec<B>>>());
256 assert!(!world.entity(parent).contains::<B>());
257
258 let children = world.get::<Children>(parent).unwrap();
259 assert_eq!(children.len(), 3);
260
261 for (i, child_entity) in children.iter().enumerate() {
262 assert_eq!(world.get::<B>(*child_entity), Some(&B(i as u8)));
263 }
264 }
265
266 #[test]
267 fn with_child_closure() {
268 let mut world = World::default();
269
270 let parent = world.spawn(ChildBundles((0..7).map(|i| B(i as u8)))).id();
271 world.flush();
273
274 assert!(!world.entity(parent).contains::<ChildBundles<B, Vec<B>>>());
275 assert!(!world.entity(parent).contains::<B>());
276
277 let children = world.get::<Children>(parent).unwrap();
278 assert_eq!(children.len(), 7);
279
280 for (i, child_entity) in children.iter().enumerate() {
281 assert_eq!(world.get::<B>(*child_entity), Some(&B(i as u8)));
282 }
283 }
284
285 #[test]
286 fn with_distinct_children() {
287 let mut world = World::default();
288
289 let parent = world.spawn((ChildBundle(A), ChildBundle(B(1)))).id();
290 world.flush();
292
293 let children = world.get::<Children>(parent).unwrap();
294 assert_eq!(children.len(), 2);
295 assert_eq!(world.get::<A>(children[0]), Some(&A));
296 assert_eq!(world.get::<B>(children[1]), Some(&B(1)));
297
298 let parent = world.spawn((ChildBundle(B(1)), ChildBundle(A))).id();
300 world.flush();
302
303 let children = world.get::<Children>(parent).unwrap();
304 assert_eq!(children.len(), 2);
305 assert_eq!(world.get::<B>(children[0]), Some(&B(1)));
306 assert_eq!(world.get::<A>(children[1]), Some(&A));
307 }
308
309 #[test]
310 fn grandchildren() {
311 let mut world = World::default();
312
313 let parent = world.spawn(ChildBundle((A, ChildBundle((A, B(3)))))).id();
314 world.flush();
316
317 let children = world.get::<Children>(parent).unwrap();
318 assert_eq!(children.len(), 1);
319
320 let child_entity = children[0];
321 assert_eq!(world.get::<A>(child_entity), Some(&A));
322
323 let grandchildren = world.get::<Children>(child_entity).unwrap();
324 assert_eq!(grandchildren.len(), 1);
325
326 let grandchild_entity = grandchildren[0];
327 assert_eq!(world.get::<A>(grandchild_entity), Some(&A));
328 assert_eq!(world.get::<B>(grandchild_entity), Some(&B(3)));
329 }
330
331 #[test]
332 fn hierarchical_bundle() {
333 let mut world = World::default();
334
335 let parent = world
336 .spawn(HierarchicalBundle {
337 a: A,
338 child: ChildBundle(ABBundle { a: A, b: B(17) }),
339 })
340 .id();
341
342 world.flush();
344
345 assert!(!world.entity(parent).contains::<ChildBundle<ABBundle>>());
346 assert!(world.entity(parent).contains::<A>());
347 assert!(!world.entity(parent).contains::<B>());
348
349 let children = world.get::<Children>(parent).unwrap();
350 assert_eq!(children.len(), 1);
351
352 let child_entity = children[0];
353 assert_eq!(world.get::<A>(child_entity), Some(&A));
354 assert_eq!(world.get::<B>(child_entity), Some(&B(17)));
355 }
356
357 #[test]
358 fn command_form() {
359 fn spawn_with_child(mut commands: Commands) -> Entity {
360 commands.spawn((A, ChildBundle(B(5)))).id()
361 }
362
363 let mut world = World::new();
364 let parent = world.run_system_once(spawn_with_child).unwrap();
365
366 assert!(!world.entity(parent).contains::<ChildBundle<B>>());
367 assert!(world.entity(parent).contains::<A>());
368 assert!(!world.entity(parent).contains::<B>());
369
370 let children = world.get::<Children>(parent).unwrap();
371 assert_eq!(children.len(), 1);
372
373 let child_entity = children[0];
374 assert_eq!(world.get::<B>(child_entity), Some(&B(5)));
375 }
376}