Skip to content

Commit e5c82f0

Browse files
Allow entities to be added to a relationship using the new spawn api (remade) (#19046)
# Objective Reopens #18961 ## Solution Copy the code ## Testing It worked in the previous pull request ## Showcase See #18961 --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent 3aa06ef commit e5c82f0

File tree

2 files changed

+214
-2
lines changed

2 files changed

+214
-2
lines changed

crates/bevy_ecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub mod prelude {
9797
common_conditions::*, ApplyDeferred, IntoScheduleConfigs, IntoSystemSet, Schedule,
9898
Schedules, SystemCondition, SystemSet,
9999
},
100-
spawn::{Spawn, SpawnRelated},
100+
spawn::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated},
101101
system::{
102102
Command, Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef,
103103
IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem,

crates/bevy_ecs/src/spawn.rs

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,80 @@ impl<R: Relationship, F: FnOnce(&mut RelatedSpawner<R>) + Send + Sync + 'static>
133133
}
134134
}
135135

136+
/// A [`SpawnableList`] that links already spawned entities to the root entity via relations of type `I`.
137+
///
138+
/// This is useful if the entity has already been spawned earlier or if you spawn multiple relationships link to the same entity at the same time.
139+
/// If you only need to do this for a single entity, consider using [`WithOneRelated`].
140+
///
141+
/// ```
142+
/// # use bevy_ecs::hierarchy::Children;
143+
/// # use bevy_ecs::spawn::{Spawn, WithRelated, SpawnRelated};
144+
/// # use bevy_ecs::name::Name;
145+
/// # use bevy_ecs::world::World;
146+
/// let mut world = World::new();
147+
///
148+
/// let child2 = world.spawn(Name::new("Child2")).id();
149+
/// let child3 = world.spawn(Name::new("Child3")).id();
150+
///
151+
/// world.spawn((
152+
/// Name::new("Root"),
153+
/// Children::spawn((
154+
/// Spawn(Name::new("Child1")),
155+
/// // This adds the already existing entities as children of Root.
156+
/// WithRelated([child2, child3].into_iter()),
157+
/// )),
158+
/// ));
159+
/// ```
160+
pub struct WithRelated<I>(pub I);
161+
162+
impl<R: Relationship, I: Iterator<Item = Entity>> SpawnableList<R> for WithRelated<I> {
163+
fn spawn(self, world: &mut World, entity: Entity) {
164+
world
165+
.entity_mut(entity)
166+
.add_related::<R>(&self.0.collect::<Vec<_>>());
167+
}
168+
169+
fn size_hint(&self) -> usize {
170+
self.0.size_hint().0
171+
}
172+
}
173+
174+
/// A wrapper over an [`Entity`] indicating that an entity should be added.
175+
/// This is intended to be used for hierarchical spawning via traits like [`SpawnableList`] and [`SpawnRelated`].
176+
///
177+
/// Unlike [`WithRelated`] this only adds one entity.
178+
///
179+
/// Also see the [`children`](crate::children) and [`related`](crate::related) macros that abstract over the [`Spawn`] API.
180+
///
181+
/// ```
182+
/// # use bevy_ecs::hierarchy::Children;
183+
/// # use bevy_ecs::spawn::{Spawn, WithOneRelated, SpawnRelated};
184+
/// # use bevy_ecs::name::Name;
185+
/// # use bevy_ecs::world::World;
186+
/// let mut world = World::new();
187+
///
188+
/// let child1 = world.spawn(Name::new("Child1")).id();
189+
///
190+
/// world.spawn((
191+
/// Name::new("Root"),
192+
/// Children::spawn((
193+
/// // This adds the already existing entity as a child of Root.
194+
/// WithOneRelated(child1),
195+
/// )),
196+
/// ));
197+
/// ```
198+
pub struct WithOneRelated(pub Entity);
199+
200+
impl<R: Relationship> SpawnableList<R> for WithOneRelated {
201+
fn spawn(self, world: &mut World, entity: Entity) {
202+
world.entity_mut(entity).add_one_related::<R>(self.0);
203+
}
204+
205+
fn size_hint(&self) -> usize {
206+
1
207+
}
208+
}
209+
136210
macro_rules! spawnable_list_impl {
137211
($($list: ident),*) => {
138212
#[expect(
@@ -267,7 +341,7 @@ pub trait SpawnRelated: RelationshipTarget {
267341
/// Returns a [`Bundle`] containing this [`RelationshipTarget`] component. It also spawns a [`SpawnableList`] of entities, each related to the bundle's entity
268342
/// via [`RelationshipTarget::Relationship`]. The [`RelationshipTarget`] (when possible) will pre-allocate space for the related entities.
269343
///
270-
/// See [`Spawn`], [`SpawnIter`], and [`SpawnWith`] for usage examples.
344+
/// See [`Spawn`], [`SpawnIter`], [`SpawnWith`], [`WithRelated`] and [`WithOneRelated`] for usage examples.
271345
fn spawn<L: SpawnableList<Self::Relationship>>(
272346
list: L,
273347
) -> SpawnRelatedBundle<Self::Relationship, L>;
@@ -485,3 +559,141 @@ macro_rules! recursive_spawn {
485559
)
486560
};
487561
}
562+
563+
#[cfg(test)]
564+
mod tests {
565+
566+
use crate::{
567+
name::Name,
568+
prelude::{ChildOf, Children, RelationshipTarget},
569+
relationship::RelatedSpawner,
570+
world::World,
571+
};
572+
573+
use super::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated};
574+
575+
#[test]
576+
fn spawn() {
577+
let mut world = World::new();
578+
579+
let parent = world
580+
.spawn((
581+
Name::new("Parent"),
582+
Children::spawn(Spawn(Name::new("Child1"))),
583+
))
584+
.id();
585+
586+
let children = world
587+
.query::<&Children>()
588+
.get(&world, parent)
589+
.expect("An entity with Children should exist");
590+
591+
assert_eq!(children.iter().count(), 1);
592+
593+
for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
594+
assert_eq!(child, &parent);
595+
}
596+
}
597+
598+
#[test]
599+
fn spawn_iter() {
600+
let mut world = World::new();
601+
602+
let parent = world
603+
.spawn((
604+
Name::new("Parent"),
605+
Children::spawn(SpawnIter(["Child1", "Child2"].into_iter().map(Name::new))),
606+
))
607+
.id();
608+
609+
let children = world
610+
.query::<&Children>()
611+
.get(&world, parent)
612+
.expect("An entity with Children should exist");
613+
614+
assert_eq!(children.iter().count(), 2);
615+
616+
for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
617+
assert_eq!(child, &parent);
618+
}
619+
}
620+
621+
#[test]
622+
fn spawn_with() {
623+
let mut world = World::new();
624+
625+
let parent = world
626+
.spawn((
627+
Name::new("Parent"),
628+
Children::spawn(SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
629+
parent.spawn(Name::new("Child1"));
630+
})),
631+
))
632+
.id();
633+
634+
let children = world
635+
.query::<&Children>()
636+
.get(&world, parent)
637+
.expect("An entity with Children should exist");
638+
639+
assert_eq!(children.iter().count(), 1);
640+
641+
for ChildOf(child) in world.query::<&ChildOf>().iter(&world) {
642+
assert_eq!(child, &parent);
643+
}
644+
}
645+
646+
#[test]
647+
fn with_related() {
648+
let mut world = World::new();
649+
650+
let child1 = world.spawn(Name::new("Child1")).id();
651+
let child2 = world.spawn(Name::new("Child2")).id();
652+
653+
let parent = world
654+
.spawn((
655+
Name::new("Parent"),
656+
Children::spawn(WithRelated([child1, child2].into_iter())),
657+
))
658+
.id();
659+
660+
let children = world
661+
.query::<&Children>()
662+
.get(&world, parent)
663+
.expect("An entity with Children should exist");
664+
665+
assert_eq!(children.iter().count(), 2);
666+
667+
assert_eq!(
668+
world.entity(child1).get::<ChildOf>(),
669+
Some(&ChildOf(parent))
670+
);
671+
assert_eq!(
672+
world.entity(child2).get::<ChildOf>(),
673+
Some(&ChildOf(parent))
674+
);
675+
}
676+
677+
#[test]
678+
fn with_one_related() {
679+
let mut world = World::new();
680+
681+
let child1 = world.spawn(Name::new("Child1")).id();
682+
683+
let parent = world
684+
.spawn((Name::new("Parent"), Children::spawn(WithOneRelated(child1))))
685+
.id();
686+
687+
let children = world
688+
.query::<&Children>()
689+
.get(&world, parent)
690+
.expect("An entity with Children should exist");
691+
692+
assert_eq!(children.iter().count(), 1);
693+
694+
assert_eq!(
695+
world.entity(child1).get::<ChildOf>(),
696+
Some(&ChildOf(parent))
697+
);
698+
}
699+
}

0 commit comments

Comments
 (0)