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 let id = entity.id();
340
341 entity.world_scope(|world: &mut World| {
342 bevy_ptr::deconstruct_moving_ptr!({
343 let Self { list, marker: _ } = effect;
344 });
345 L::spawn(list, world, id);
346 });
347 }
348}
349
350pub struct SpawnOneRelated<R: Relationship, B: Bundle> {
356 bundle: B,
357 marker: PhantomData<R>,
358}
359
360impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
361 type Effect = Self;
362
363 unsafe fn get_components(
364 ptr: MovingPtr<'_, Self>,
365 func: &mut impl FnMut(crate::component::StorageType, bevy_ptr::OwningPtr<'_>),
366 ) {
367 let target = <R::RelationshipTarget as RelationshipTarget>::with_capacity(1);
368 move_as_ptr!(target);
369 unsafe { <R::RelationshipTarget as DynamicBundle>::get_components(target, func) };
376 mem::forget(ptr);
378 }
379
380 unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
381 let effect = unsafe { ptr.assume_init() };
384 let effect = effect.read();
385 entity.with_related::<R>(effect.bundle);
386 }
387}
388
389unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
391 fn component_ids(
392 components: &mut crate::component::ComponentsRegistrator,
393 ) -> impl Iterator<Item = crate::component::ComponentId> + use<R, B> {
394 <R::RelationshipTarget as Bundle>::component_ids(components)
395 }
396
397 fn get_component_ids(
398 components: &crate::component::Components,
399 ) -> impl Iterator<Item = Option<crate::component::ComponentId>> {
400 <R::RelationshipTarget as Bundle>::get_component_ids(components)
401 }
402}
403
404pub trait SpawnRelated: RelationshipTarget {
409 fn spawn<L: SpawnableList<Self::Relationship>>(
414 list: L,
415 ) -> SpawnRelatedBundle<Self::Relationship, L>;
416
417 fn spawn_one<B: Bundle>(bundle: B) -> SpawnOneRelated<Self::Relationship, B>;
432}
433
434impl<T: RelationshipTarget> SpawnRelated for T {
435 fn spawn<L: SpawnableList<Self::Relationship>>(
436 list: L,
437 ) -> SpawnRelatedBundle<Self::Relationship, L> {
438 SpawnRelatedBundle {
439 list,
440 marker: PhantomData,
441 }
442 }
443
444 fn spawn_one<B: Bundle>(bundle: B) -> SpawnOneRelated<Self::Relationship, B> {
445 SpawnOneRelated {
446 bundle,
447 marker: PhantomData,
448 }
449 }
450}
451
452#[macro_export]
480macro_rules! related {
481 ($relationship_target:ty [$($child:expr),*$(,)?]) => {
482 <$relationship_target>::spawn($crate::recursive_spawn!($($child),*))
483 };
484}
485
486#[macro_export]
497#[doc(hidden)]
498macro_rules! recursive_spawn {
499 () => { () };
501 ($a:expr) => {
502 $crate::spawn::Spawn($a)
503 };
504 ($a:expr, $b:expr) => {
505 (
506 $crate::spawn::Spawn($a),
507 $crate::spawn::Spawn($b),
508 )
509 };
510 ($a:expr, $b:expr, $c:expr) => {
511 (
512 $crate::spawn::Spawn($a),
513 $crate::spawn::Spawn($b),
514 $crate::spawn::Spawn($c),
515 )
516 };
517 ($a:expr, $b:expr, $c:expr, $d:expr) => {
518 (
519 $crate::spawn::Spawn($a),
520 $crate::spawn::Spawn($b),
521 $crate::spawn::Spawn($c),
522 $crate::spawn::Spawn($d),
523 )
524 };
525 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {
526 (
527 $crate::spawn::Spawn($a),
528 $crate::spawn::Spawn($b),
529 $crate::spawn::Spawn($c),
530 $crate::spawn::Spawn($d),
531 $crate::spawn::Spawn($e),
532 )
533 };
534 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr) => {
535 (
536 $crate::spawn::Spawn($a),
537 $crate::spawn::Spawn($b),
538 $crate::spawn::Spawn($c),
539 $crate::spawn::Spawn($d),
540 $crate::spawn::Spawn($e),
541 $crate::spawn::Spawn($f),
542 )
543 };
544 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr) => {
545 (
546 $crate::spawn::Spawn($a),
547 $crate::spawn::Spawn($b),
548 $crate::spawn::Spawn($c),
549 $crate::spawn::Spawn($d),
550 $crate::spawn::Spawn($e),
551 $crate::spawn::Spawn($f),
552 $crate::spawn::Spawn($g),
553 )
554 };
555 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => {
556 (
557 $crate::spawn::Spawn($a),
558 $crate::spawn::Spawn($b),
559 $crate::spawn::Spawn($c),
560 $crate::spawn::Spawn($d),
561 $crate::spawn::Spawn($e),
562 $crate::spawn::Spawn($f),
563 $crate::spawn::Spawn($g),
564 $crate::spawn::Spawn($h),
565 )
566 };
567 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr) => {
568 (
569 $crate::spawn::Spawn($a),
570 $crate::spawn::Spawn($b),
571 $crate::spawn::Spawn($c),
572 $crate::spawn::Spawn($d),
573 $crate::spawn::Spawn($e),
574 $crate::spawn::Spawn($f),
575 $crate::spawn::Spawn($g),
576 $crate::spawn::Spawn($h),
577 $crate::spawn::Spawn($i),
578 )
579 };
580 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr) => {
581 (
582 $crate::spawn::Spawn($a),
583 $crate::spawn::Spawn($b),
584 $crate::spawn::Spawn($c),
585 $crate::spawn::Spawn($d),
586 $crate::spawn::Spawn($e),
587 $crate::spawn::Spawn($f),
588 $crate::spawn::Spawn($g),
589 $crate::spawn::Spawn($h),
590 $crate::spawn::Spawn($i),
591 $crate::spawn::Spawn($j),
592 )
593 };
594 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr) => {
595 (
596 $crate::spawn::Spawn($a),
597 $crate::spawn::Spawn($b),
598 $crate::spawn::Spawn($c),
599 $crate::spawn::Spawn($d),
600 $crate::spawn::Spawn($e),
601 $crate::spawn::Spawn($f),
602 $crate::spawn::Spawn($g),
603 $crate::spawn::Spawn($h),
604 $crate::spawn::Spawn($i),
605 $crate::spawn::Spawn($j),
606 $crate::spawn::Spawn($k),
607 )
608 };
609
610 (
612 $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr,
613 $g:expr, $h:expr, $i:expr, $j:expr, $k:expr, $($rest:expr),*
614 ) => {
615 (
616 $crate::spawn::Spawn($a),
617 $crate::spawn::Spawn($b),
618 $crate::spawn::Spawn($c),
619 $crate::spawn::Spawn($d),
620 $crate::spawn::Spawn($e),
621 $crate::spawn::Spawn($f),
622 $crate::spawn::Spawn($g),
623 $crate::spawn::Spawn($h),
624 $crate::spawn::Spawn($i),
625 $crate::spawn::Spawn($j),
626 $crate::spawn::Spawn($k),
627 $crate::recursive_spawn!($($rest),*)
628 )
629 };
630}
631
632#[cfg(test)]
633mod tests {
634
635 use crate::{
636 name::Name,
637 prelude::{ChildOf, Children, RelationshipTarget},
638 relationship::RelatedSpawner,
639 world::World,
640 };
641
642 use super::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated};
643
644 #[test]
645 fn spawn() {
646 let mut world = World::new();
647
648 let parent = world
649 .spawn((
650 Name::new("Parent"),
651 Children::spawn(Spawn(Name::new("Child1"))),
652 ))
653 .id();
654
655 let children = world
656 .query::<&Children>()
657 .get(&world, parent)
658 .expect("An entity with Children should exist");
659
660 assert_eq!(children.iter().count(), 1);
661
662 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
663 assert_eq!(child, &parent);
664 }
665 }
666
667 #[test]
668 fn spawn_iter() {
669 let mut world = World::new();
670
671 let parent = world
672 .spawn((
673 Name::new("Parent"),
674 Children::spawn(SpawnIter(["Child1", "Child2"].into_iter().map(Name::new))),
675 ))
676 .id();
677
678 let children = world
679 .query::<&Children>()
680 .get(&world, parent)
681 .expect("An entity with Children should exist");
682
683 assert_eq!(children.iter().count(), 2);
684
685 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
686 assert_eq!(child, &parent);
687 }
688 }
689
690 #[test]
691 fn spawn_with() {
692 let mut world = World::new();
693
694 let parent = world
695 .spawn((
696 Name::new("Parent"),
697 Children::spawn(SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
698 parent.spawn(Name::new("Child1"));
699 })),
700 ))
701 .id();
702
703 let children = world
704 .query::<&Children>()
705 .get(&world, parent)
706 .expect("An entity with Children should exist");
707
708 assert_eq!(children.iter().count(), 1);
709
710 for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
711 assert_eq!(child, &parent);
712 }
713 }
714
715 #[test]
716 fn with_related() {
717 let mut world = World::new();
718
719 let child1 = world.spawn(Name::new("Child1")).id();
720 let child2 = world.spawn(Name::new("Child2")).id();
721
722 let parent = world
723 .spawn((
724 Name::new("Parent"),
725 Children::spawn(WithRelated::new([child1, child2])),
726 ))
727 .id();
728
729 let children = world
730 .query::<&Children>()
731 .get(&world, parent)
732 .expect("An entity with Children should exist");
733
734 assert_eq!(children.iter().count(), 2);
735
736 assert_eq!(
737 world.entity(child1).get::<ChildOf>(),
738 Some(&ChildOf(parent))
739 );
740 assert_eq!(
741 world.entity(child2).get::<ChildOf>(),
742 Some(&ChildOf(parent))
743 );
744 }
745
746 #[test]
747 fn with_one_related() {
748 let mut world = World::new();
749
750 let child1 = world.spawn(Name::new("Child1")).id();
751
752 let parent = world
753 .spawn((Name::new("Parent"), Children::spawn(WithOneRelated(child1))))
754 .id();
755
756 let children = world
757 .query::<&Children>()
758 .get(&world, parent)
759 .expect("An entity with Children should exist");
760
761 assert_eq!(children.iter().count(), 1);
762
763 assert_eq!(
764 world.entity(child1).get::<ChildOf>(),
765 Some(&ChildOf(parent))
766 );
767 }
768}