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