Change with_related to work with a Bundle and added with_relationships method (#18699)

# Objective

Fixes #18678

## Solution

Moved the current `with_related` method to `with_relationships` and
added a new `with_related` that uses a bundle.

I'm not entirely sold on the name just yet, if anyone has any ideas let
me know.

## Testing

I wasn't able to test these changes because it crashed my computer every
time I tried (fun). But there don't seem to be any tests that use the
old `with_related` method so it should be fine, hopefully

## Showcase

```rust
commands.spawn_empty()
    .with_related::<Relationship>(Name::new("Related thingy"))
    .with_relationships(|rel| {
        rel.spawn(Name::new("Second related thingy"));
    });
```

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Freyja-moth 2025-04-09 03:34:49 +01:00 committed by François Mockers
parent 83a78d8583
commit 772524b3a6
5 changed files with 29 additions and 14 deletions

View File

@ -268,7 +268,7 @@ impl<'w> EntityWorldMut<'w> {
/// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`]. /// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
/// See also [`with_related`](Self::with_related). /// See also [`with_related`](Self::with_related).
pub fn with_children(&mut self, func: impl FnOnce(&mut ChildSpawner)) -> &mut Self { pub fn with_children(&mut self, func: impl FnOnce(&mut ChildSpawner)) -> &mut Self {
self.with_related(func); self.with_related_entities(func);
self self
} }
@ -352,7 +352,7 @@ impl<'a> EntityCommands<'a> {
&mut self, &mut self,
func: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>), func: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>),
) -> &mut Self { ) -> &mut Self {
self.with_related(func); self.with_related_entities(func);
self self
} }
@ -406,8 +406,7 @@ impl<'a> EntityCommands<'a> {
/// ///
/// [`with_children`]: EntityCommands::with_children /// [`with_children`]: EntityCommands::with_children
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self { pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
let parent = self.id(); self.with_related::<ChildOf>(bundle);
self.commands.spawn((bundle, ChildOf(parent)));
self self
} }

View File

@ -1654,7 +1654,7 @@ mod tests {
fn on_add(trigger: Trigger<OnAdd, A>, mut commands: Commands) { fn on_add(trigger: Trigger<OnAdd, A>, mut commands: Commands) {
commands commands
.entity(trigger.target()) .entity(trigger.target())
.with_related::<crate::hierarchy::ChildOf>(|rsc| { .with_related_entities::<crate::hierarchy::ChildOf>(|rsc| {
rsc.spawn_empty(); rsc.spawn_empty();
}); });
} }

View File

@ -13,8 +13,17 @@ use core::{marker::PhantomData, mem};
use super::OrderedRelationshipSourceCollection; use super::OrderedRelationshipSourceCollection;
impl<'w> EntityWorldMut<'w> { impl<'w> EntityWorldMut<'w> {
/// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle
pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {
let parent = self.id();
self.world_scope(|world| {
world.spawn((bundle, R::from(parent)));
});
self
}
/// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`]. /// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].
pub fn with_related<R: Relationship>( pub fn with_related_entities<R: Relationship>(
&mut self, &mut self,
func: impl FnOnce(&mut RelatedSpawner<R>), func: impl FnOnce(&mut RelatedSpawner<R>),
) -> &mut Self { ) -> &mut Self {
@ -322,8 +331,15 @@ impl<'w> EntityWorldMut<'w> {
} }
impl<'a> EntityCommands<'a> { impl<'a> EntityCommands<'a> {
/// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle
pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {
let parent = self.id();
self.commands.spawn((bundle, R::from(parent)));
self
}
/// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`]. /// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].
pub fn with_related<R: Relationship>( pub fn with_related_entities<R: Relationship>(
&mut self, &mut self,
func: impl FnOnce(&mut RelatedSpawnerCommands<R>), func: impl FnOnce(&mut RelatedSpawnerCommands<R>),
) -> &mut Self { ) -> &mut Self {

View File

@ -125,7 +125,7 @@ impl<R: Relationship, F: FnOnce(&mut RelatedSpawner<R>) + Send + Sync + 'static>
for SpawnWith<F> for SpawnWith<F>
{ {
fn spawn(self, world: &mut World, entity: Entity) { fn spawn(self, world: &mut World, entity: Entity) {
world.entity_mut(entity).with_related(self.0); world.entity_mut(entity).with_related_entities(self.0);
} }
fn size_hint(&self) -> usize { fn size_hint(&self) -> usize {
@ -235,9 +235,7 @@ pub struct SpawnOneRelated<R: Relationship, B: Bundle> {
impl<R: Relationship, B: Bundle> BundleEffect for SpawnOneRelated<R, B> { impl<R: Relationship, B: Bundle> BundleEffect for SpawnOneRelated<R, B> {
fn apply(self, entity: &mut EntityWorldMut) { fn apply(self, entity: &mut EntityWorldMut) {
entity.with_related::<R>(|s| { entity.with_related::<R>(self.bundle);
s.spawn(self.bundle);
});
} }
} }

View File

@ -53,12 +53,14 @@ fn main() {
// Relations are just components, so we can add them into the bundle that we're spawning. // Relations are just components, so we can add them into the bundle that we're spawning.
let bob = commands.spawn((Name::new("Bob"), Targeting(alice))).id(); let bob = commands.spawn((Name::new("Bob"), Targeting(alice))).id();
// The `with_related` helper method on `EntityCommands` can be used to add relations in a more ergonomic way. // The `with_related` and `with_relationships` helper methods on `EntityCommands` can be used to add relations in a more ergonomic way.
let charlie = commands let charlie = commands
.spawn((Name::new("Charlie"), Targeting(bob))) .spawn((Name::new("Charlie"), Targeting(bob)))
// The `with_related` method will automatically add the `Targeting` component to any entities spawned within the closure, // The `with_related` method will spawn a bundle with `Targeting` relationship
.with_related::<Targeting>(Name::new("James"))
// The `with_relationships` method will automatically add the `Targeting` component to any entities spawned within the closure,
// targeting the entity that we're calling `with_related` on. // targeting the entity that we're calling `with_related` on.
.with_related::<Targeting>(|related_spawner_commands| { .with_related_entities::<Targeting>(|related_spawner_commands| {
// We could spawn multiple entities here, and they would all target `charlie`. // We could spawn multiple entities here, and they would all target `charlie`.
related_spawner_commands.spawn(Name::new("Devon")); related_spawner_commands.spawn(Name::new("Devon"));
}) })