1use crate::{
5 bundle::{Bundle, DynamicBundle, InsertMode, NoBundleEffect},
6 change_detection::MaybeLocation,
7 entity::Entity,
8 relationship::{RelatedSpawner, Relationship, RelationshipHookMode, RelationshipTarget},
9 world::{EntityWorldMut, World},
10};
11use alloc::vec::Vec;
12use bevy_ptr::{move_as_ptr, MovingPtr};
13use core::{
14 marker::PhantomData,
15 mem::{self, MaybeUninit},
16};
17use variadics_please::all_tuples_enumerated;
18
19pub struct Spawn<B: Bundle>(pub B);
42
43pub trait SpawnableList<R>: Sized {
46 fn spawn(this: MovingPtr<'_, Self>, world: &mut World, entity: Entity);
52
53 fn size_hint(&self) -> usize;
56}
57
58impl<R: Relationship, B: Bundle<Effect: NoBundleEffect>> SpawnableList<R> for Vec<B> {
59 fn spawn(ptr: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
60 let mapped_bundles = ptr.read().into_iter().map(|b| (R::from(entity), b));
61 world.spawn_batch(mapped_bundles);
62 }
63
64 fn size_hint(&self) -> usize {
65 self.len()
66 }
67}
68
69impl<R: Relationship, B: Bundle> SpawnableList<R> for Spawn<B> {
70 fn spawn(this: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
71 #[track_caller]
72 fn spawn<B: Bundle, R: Relationship>(
73 this: MovingPtr<'_, Spawn<B>>,
74 world: &mut World,
75 entity: Entity,
76 ) {
77 let caller = MaybeLocation::caller();
78
79 bevy_ptr::deconstruct_moving_ptr!({
80 let Spawn { 0: bundle } = this;
81 });
82
83 let r = R::from(entity);
84 move_as_ptr!(r);
85 let mut entity = world.spawn_with_caller(r, caller);
86
87 entity.insert_with_caller(
88 bundle,
89 InsertMode::Replace,
90 caller,
91 RelationshipHookMode::Run,
92 );
93 }
94
95 spawn::<B, R>(this, world, entity);
96 }
97
98 fn size_hint(&self) -> usize {
99 1
100 }
101}
102
103pub struct SpawnIter<I>(pub I);
120
121impl<R: Relationship, I: Iterator<Item = B> + Send + Sync + 'static, B: Bundle> SpawnableList<R>
122 for SpawnIter<I>
123{
124 fn spawn(mut this: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
125 for bundle in &mut this.0 {
126 world.spawn((R::from(entity), bundle));
127 }
128 }
129
130 fn size_hint(&self) -> usize {
131 self.0.size_hint().0
132 }
133}
134
135pub struct SpawnWith<F>(pub F);
156
157impl<R: Relationship, F: FnOnce(&mut RelatedSpawner<R>) + Send + Sync + 'static> SpawnableList<R>
158 for SpawnWith<F>
159{
160 fn spawn(this: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
161 world
162 .entity_mut(entity)
163 .with_related_entities(this.read().0);
164 }
165
166 fn size_hint(&self) -> usize {
167 1
168 }
169}
170
171pub struct WithRelated<I>(pub I);
196
197impl<I> WithRelated<I> {
198 pub fn new(iter: impl IntoIterator<IntoIter = I>) -> Self {
200 Self(iter.into_iter())
201 }
202}
203
204impl<R: Relationship, I: Iterator<Item = Entity>> SpawnableList<R> for WithRelated<I> {
205 fn spawn(mut this: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
206 let related = (&mut this.0).collect::<Vec<_>>();
207 world.entity_mut(entity).add_related::<R>(&related);
208 }
209
210 fn size_hint(&self) -> usize {
211 self.0.size_hint().0
212 }
213}
214
215pub struct WithOneRelated(pub Entity);
240
241impl<R: Relationship> SpawnableList<R> for WithOneRelated {
242 fn spawn(this: MovingPtr<'_, Self>, world: &mut World, entity: Entity) {
243 world.entity_mut(entity).add_one_related::<R>(this.read().0);
244 }
245
246 fn size_hint(&self) -> usize {
247 1
248 }
249}
250
251macro_rules! spawnable_list_impl {
252 ($(#[$meta:meta])* $(($index:tt, $list: ident, $alias: ident)),*) => {
253 $(#[$meta])*
254 impl<R: Relationship, $($list: SpawnableList<R>),*> SpawnableList<R> for ($($list,)*) {
255 #[expect(
256 clippy::allow_attributes,
257 reason = "This is a tuple-related macro; as such, the lints below may not always apply."
258 )]
259 #[allow(unused_unsafe, reason = "The empty tuple will leave the unsafe blocks unused.")]
260 fn spawn(_this: MovingPtr<'_, Self>, _world: &mut World, _entity: Entity)
261 where
262 Self: Sized,
263 {
264 bevy_ptr::deconstruct_moving_ptr!({
265 let tuple { $($index: $alias),* } = _this;
266 });
267 $( SpawnableList::<R>::spawn($alias, _world, _entity); )*
268 }
269
270 fn size_hint(&self) -> usize {
271 let ($($alias,)*) = self;
272 0 $(+ $alias.size_hint())*
273 }
274 }
275 }
276}
277
278all_tuples_enumerated!(
279 #[doc(fake_variadic)]
280 spawnable_list_impl,
281 0,
282 12,
283 P,
284 field_
285);
286
287pub struct SpawnRelatedBundle<R: Relationship, L: SpawnableList<R>> {
293 list: L,
294 marker: PhantomData<R>,
295}
296
297unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> Bundle
299 for SpawnRelatedBundle<R, L>
300{
301 fn component_ids(
302 components: &mut crate::component::ComponentsRegistrator,
303 ) -> impl Iterator<Item = crate::component::ComponentId> + use<R, L> {
304 <R::RelationshipTarget as Bundle>::component_ids(components)
305 }
306
307 fn get_component_ids(
308 components: &crate::component::Components,
309 ) -> impl Iterator<Item = Option<crate::component::ComponentId>> {
310 <R::RelationshipTarget as Bundle>::get_component_ids(components)
311 }
312}
313
314impl<R: Relationship, L: SpawnableList<R>> DynamicBundle for SpawnRelatedBundle<R, L> {
315 type Effect = Self;
316
317 unsafe fn get_components(
318 ptr: MovingPtr<'_, Self>,
319 func: &mut impl FnMut(crate::component::StorageType, bevy_ptr::OwningPtr<'_>),
320 ) {
321 let target =
322 <R::RelationshipTarget as RelationshipTarget>::with_capacity(ptr.list.size_hint());
323 move_as_ptr!(target);
324 unsafe { <R::RelationshipTarget as DynamicBundle>::get_components(target, func) };
331 mem::forget(ptr);
333 }
334
335 unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
336 let effect = unsafe { ptr.assume_init() };
339 bevy_ptr::deconstruct_moving_ptr!({
340 let Self { list, marker: _ } = effect;
341 });
342 let id = entity.id();
343
344 if entity.is_despawned() {
345 return;
346 }
347
348 entity.world_scope(|world: &mut World| {
349 L::spawn(list, world, id);
350 });
351 }
352}
353
354pub struct SpawnOneRelated<R: Relationship, B: Bundle> {
360 bundle: B,
361 marker: PhantomData<R>,
362}
363
364impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
365 type Effect = Self;
366
367 unsafe fn get_components(
368 ptr: MovingPtr<'_, Self>,
369 func: &mut impl FnMut(crate::component::StorageType, bevy_ptr::OwningPtr<'_>),
370 ) {
371 let target = <R::RelationshipTarget as RelationshipTarget>::with_capacity(1);
372 move_as_ptr!(target);
373 unsafe { <R::RelationshipTarget as DynamicBundle>::get_components(target, func) };
380 mem::forget(ptr);
382 }
383
384 unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
385 let effect = unsafe { ptr.assume_init() };
388 if entity.is_despawned() {
389 return;
390 }
391
392 let effect = effect.read();
393 entity.with_related::<R>(effect.bundle);
394 }
395}
396
397unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
399 fn component_ids(
400 components: &mut crate::component::ComponentsRegistrator,
401 ) -> impl Iterator<Item = crate::component::ComponentId> + use<R, B> {
402 <R::RelationshipTarget as Bundle>::component_ids(components)
403 }
404
405 fn get_component_ids(
406 components: &crate::component::Components,
407 ) -> impl Iterator<Item = Option<crate::component::ComponentId>> {
408 <R::RelationshipTarget as Bundle>::get_component_ids(components)
409 }
410}
411
412pub trait SpawnRelated: RelationshipTarget {
417 fn spawn<L: SpawnableList<Self::Relationship>>(
422 list: L,
423 ) -> SpawnRelatedBundle<Self::Relationship, L>;
424
425 fn spawn_one<B: Bundle>(bundle: B) -> SpawnOneRelated<Self::Relationship, B>;
440}
441
442impl<T: RelationshipTarget> SpawnRelated for T {
443 fn spawn<L: SpawnableList<Self::Relationship>>(
444 list: L,
445 ) -> SpawnRelatedBundle<Self::Relationship, L> {
446 SpawnRelatedBundle {
447 list,
448 marker: PhantomData,
449 }
450 }
451
452 fn spawn_one<B: Bundle>(bundle: B) -> SpawnOneRelated<Self::Relationship, B> {
453 SpawnOneRelated {
454 bundle,
455 marker: PhantomData,
456 }
457 }
458}
459
460#[macro_export]
487macro_rules! related {
488 ($relationship_target:ty [$($child:expr),*$(,)?]) => {
489 <$relationship_target as $crate::spawn::SpawnRelated>::spawn($crate::recursive_spawn!($($child),*))
490 };
491}
492
493#[macro_export]
504#[doc(hidden)]
505macro_rules! recursive_spawn {
506 () => { () };
508 ($a:expr) => {
509 $crate::spawn::Spawn($a)
510 };
511 ($a:expr, $b:expr) => {
512 (
513 $crate::spawn::Spawn($a),
514 $crate::spawn::Spawn($b),
515 )
516 };
517 ($a:expr, $b:expr, $c:expr) => {
518 (
519 $crate::spawn::Spawn($a),
520 $crate::spawn::Spawn($b),
521 $crate::spawn::Spawn($c),
522 )
523 };
524 ($a:expr, $b:expr, $c:expr, $d:expr) => {
525 (
526 $crate::spawn::Spawn($a),
527 $crate::spawn::Spawn($b),
528 $crate::spawn::Spawn($c),
529 $crate::spawn::Spawn($d),
530 )
531 };
532 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {
533 (
534 $crate::spawn::Spawn($a),
535 $crate::spawn::Spawn($b),
536 $crate::spawn::Spawn($c),
537 $crate::spawn::Spawn($d),
538 $crate::spawn::Spawn($e),
539 )
540 };
541 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr) => {
542 (
543 $crate::spawn::Spawn($a),
544 $crate::spawn::Spawn($b),
545 $crate::spawn::Spawn($c),
546 $crate::spawn::Spawn($d),
547 $crate::spawn::Spawn($e),
548 $crate::spawn::Spawn($f),
549 )
550 };
551 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr) => {
552 (
553 $crate::spawn::Spawn($a),
554 $crate::spawn::Spawn($b),
555 $crate::spawn::Spawn($c),
556 $crate::spawn::Spawn($d),
557 $crate::spawn::Spawn($e),
558 $crate::spawn::Spawn($f),
559 $crate::spawn::Spawn($g),
560 )
561 };
562 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => {
563 (
564 $crate::spawn::Spawn($a),
565 $crate::spawn::Spawn($b),
566 $crate::spawn::Spawn($c),
567 $crate::spawn::Spawn($d),
568 $crate::spawn::Spawn($e),
569 $crate::spawn::Spawn($f),
570 $crate::spawn::Spawn($g),
571 $crate::spawn::Spawn($h),
572 )
573 };
574 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr) => {
575 (
576 $crate::spawn::Spawn($a),
577 $crate::spawn::Spawn($b),
578 $crate::spawn::Spawn($c),
579 $crate::spawn::Spawn($d),
580 $crate::spawn::Spawn($e),
581 $crate::spawn::Spawn($f),
582 $crate::spawn::Spawn($g),
583 $crate::spawn::Spawn($h),
584 $crate::spawn::Spawn($i),
585 )
586 };
587 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr) => {
588 (
589 $crate::spawn::Spawn($a),
590 $crate::spawn::Spawn($b),
591 $crate::spawn::Spawn($c),
592 $crate::spawn::Spawn($d),
593 $crate::spawn::Spawn($e),
594 $crate::spawn::Spawn($f),
595 $crate::spawn::Spawn($g),
596 $crate::spawn::Spawn($h),
597 $crate::spawn::Spawn($i),
598 $crate::spawn::Spawn($j),
599 )
600 };
601 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr) => {
602 (
603 $crate::spawn::Spawn($a),
604 $crate::spawn::Spawn($b),
605 $crate::spawn::Spawn($c),
606 $crate::spawn::Spawn($d),
607 $crate::spawn::Spawn($e),
608 $crate::spawn::Spawn($f),
609 $crate::spawn::Spawn($g),
610 $crate::spawn::Spawn($h),
611 $crate::spawn::Spawn($i),
612 $crate::spawn::Spawn($j),
613 $crate::spawn::Spawn($k),
614 )
615 };
616
617 (
619 $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr,
620 $g:expr, $h:expr, $i:expr, $j:expr, $k:expr, $($rest:expr),*
621 ) => {
622 (
623 $crate::spawn::Spawn($a),
624 $crate::spawn::Spawn($b),
625 $crate::spawn::Spawn($c),
626 $crate::spawn::Spawn($d),
627 $crate::spawn::Spawn($e),
628 $crate::spawn::Spawn($f),
629 $crate::spawn::Spawn($g),
630 $crate::spawn::Spawn($h),
631 $crate::spawn::Spawn($i),
632 $crate::spawn::Spawn($j),
633 $crate::spawn::Spawn($k),
634 $crate::recursive_spawn!($($rest),*)
635 )
636 };
637}
638
639#[cfg(test)]
640mod tests {
641
642 use crate::{
643 name::Name,
644 prelude::{ChildOf, Children, RelationshipTarget},
645 relationship::RelatedSpawner,
646 world::World,
647 };
648
649 use super::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated};
650
651 #[test]
652 fn spawn() {
653 let mut world = World::new();
654
655 let parent = world
656 .spawn((
657 Name::new("Parent"),
658 Children::spawn(Spawn(Name::new("Child1"))),
659 ))
660 .id();
661
662 let children = world
663 .query::<&Children>()
664 .get(&world, parent)
665 .expect("An entity with Children should exist");
666
667 assert_eq!(children.iter().count(), 1);
668
669 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
670 assert_eq!(child, &parent);
671 }
672 }
673
674 #[test]
675 fn spawn_iter() {
676 let mut world = World::new();
677
678 let parent = world
679 .spawn((
680 Name::new("Parent"),
681 Children::spawn(SpawnIter(["Child1", "Child2"].into_iter().map(Name::new))),
682 ))
683 .id();
684
685 let children = world
686 .query::<&Children>()
687 .get(&world, parent)
688 .expect("An entity with Children should exist");
689
690 assert_eq!(children.iter().count(), 2);
691
692 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
693 assert_eq!(child, &parent);
694 }
695 }
696
697 #[test]
698 fn spawn_with() {
699 let mut world = World::new();
700
701 let parent = world
702 .spawn((
703 Name::new("Parent"),
704 Children::spawn(SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
705 parent.spawn(Name::new("Child1"));
706 })),
707 ))
708 .id();
709
710 let children = world
711 .query::<&Children>()
712 .get(&world, parent)
713 .expect("An entity with Children should exist");
714
715 assert_eq!(children.iter().count(), 1);
716
717 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
718 assert_eq!(child, &parent);
719 }
720 }
721
722 #[test]
723 fn with_related() {
724 let mut world = World::new();
725
726 let child1 = world.spawn(Name::new("Child1")).id();
727 let child2 = world.spawn(Name::new("Child2")).id();
728
729 let parent = world
730 .spawn((
731 Name::new("Parent"),
732 Children::spawn(WithRelated::new([child1, child2])),
733 ))
734 .id();
735
736 let children = world
737 .query::<&Children>()
738 .get(&world, parent)
739 .expect("An entity with Children should exist");
740
741 assert_eq!(children.iter().count(), 2);
742
743 assert_eq!(
744 world.entity(child1).get::<ChildOf>(),
745 Some(&ChildOf(parent))
746 );
747 assert_eq!(
748 world.entity(child2).get::<ChildOf>(),
749 Some(&ChildOf(parent))
750 );
751 }
752
753 #[test]
754 fn with_one_related() {
755 let mut world = World::new();
756
757 let child1 = world.spawn(Name::new("Child1")).id();
758
759 let parent = world
760 .spawn((Name::new("Parent"), Children::spawn(WithOneRelated(child1))))
761 .id();
762
763 let children = world
764 .query::<&Children>()
765 .get(&world, parent)
766 .expect("An entity with Children should exist");
767
768 assert_eq!(children.iter().count(), 1);
769
770 assert_eq!(
771 world.entity(child1).get::<ChildOf>(),
772 Some(&ChildOf(parent))
773 );
774 }
775}