bevy_ecs/relationship/
related_methods.rs

1use crate::{
2    bundle::Bundle,
3    entity::{hash_set::EntityHashSet, Entity},
4    relationship::{
5        Relationship, RelationshipHookMode, RelationshipSourceCollection, RelationshipTarget,
6    },
7    system::{Commands, EntityCommands},
8    world::{EntityWorldMut, World},
9};
10use bevy_platform::prelude::{Box, Vec};
11use core::{marker::PhantomData, mem};
12
13use super::OrderedRelationshipSourceCollection;
14
15impl<'w> EntityWorldMut<'w> {
16    /// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle
17    pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {
18        let parent = self.id();
19        self.world_scope(|world| {
20            world.spawn((bundle, R::from(parent)));
21        });
22        self
23    }
24
25    /// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].
26    pub fn with_related_entities<R: Relationship>(
27        &mut self,
28        func: impl FnOnce(&mut RelatedSpawner<R>),
29    ) -> &mut Self {
30        let parent = self.id();
31        self.world_scope(|world| {
32            func(&mut RelatedSpawner::new(world, parent));
33        });
34        self
35    }
36
37    /// Relates the given entities to this entity with the relation `R`.
38    ///
39    /// See [`add_one_related`](Self::add_one_related) if you want relate only one entity.
40    pub fn add_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
41        let id = self.id();
42        self.world_scope(|world| {
43            for related in related {
44                world.entity_mut(*related).insert(R::from(id));
45            }
46        });
47        self
48    }
49
50    /// Relates the given entities to this entity with the relation `R`, starting at this particular index.
51    ///
52    /// If the `related` has duplicates, a related entity will take the index of its last occurrence in `related`.
53    /// If the indices go out of bounds, they will be clamped into bounds.
54    /// This will not re-order existing related entities unless they are in `related`.
55    ///
56    /// # Example
57    ///
58    /// ```
59    /// use bevy_ecs::prelude::*;
60    ///
61    /// let mut world = World::new();
62    /// let e0 = world.spawn_empty().id();
63    /// let e1 = world.spawn_empty().id();
64    /// let e2 = world.spawn_empty().id();
65    /// let e3 = world.spawn_empty().id();
66    /// let e4 = world.spawn_empty().id();
67    ///
68    /// let mut main_entity = world.spawn_empty();
69    /// main_entity.add_related::<ChildOf>(&[e0, e1, e2, e2]);
70    /// main_entity.insert_related::<ChildOf>(1, &[e0, e3, e4, e4]);
71    /// let main_id = main_entity.id();
72    ///
73    /// let relationship_source = main_entity.get::<Children>().unwrap().collection();
74    /// assert_eq!(relationship_source, &[e1, e0, e3, e2, e4]);
75    /// ```
76    pub fn insert_related<R: Relationship>(&mut self, index: usize, related: &[Entity]) -> &mut Self
77    where
78        <R::RelationshipTarget as RelationshipTarget>::Collection:
79            OrderedRelationshipSourceCollection,
80    {
81        let id = self.id();
82        self.world_scope(|world| {
83            for (offset, related) in related.iter().enumerate() {
84                let index = index + offset;
85                if world
86                    .get::<R>(*related)
87                    .is_some_and(|relationship| relationship.get() == id)
88                {
89                    world
90                        .get_mut::<R::RelationshipTarget>(id)
91                        .expect("hooks should have added relationship target")
92                        .collection_mut_risky()
93                        .place(*related, index);
94                } else {
95                    world.entity_mut(*related).insert(R::from(id));
96                    world
97                        .get_mut::<R::RelationshipTarget>(id)
98                        .expect("hooks should have added relationship target")
99                        .collection_mut_risky()
100                        .place_most_recent(index);
101                }
102            }
103        });
104
105        self
106    }
107
108    /// Removes the relation `R` between this entity and the given entities.
109    pub fn remove_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
110        let id = self.id();
111        self.world_scope(|world| {
112            for related in related {
113                if world
114                    .get::<R>(*related)
115                    .is_some_and(|relationship| relationship.get() == id)
116                {
117                    world.entity_mut(*related).remove::<R>();
118                }
119            }
120        });
121
122        self
123    }
124
125    /// Replaces all the related entities with a new set of entities.
126    pub fn replace_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
127        type Collection<R> =
128            <<R as Relationship>::RelationshipTarget as RelationshipTarget>::Collection;
129
130        if related.is_empty() {
131            self.remove::<R::RelationshipTarget>();
132
133            return self;
134        }
135
136        let Some(mut existing_relations) = self.get_mut::<R::RelationshipTarget>() else {
137            return self.add_related::<R>(related);
138        };
139
140        // We take the collection here so we can modify it without taking the component itself (this would create archetype move).
141        // SAFETY: We eventually return the correctly initialized collection into the target.
142        let mut existing_relations = mem::replace(
143            existing_relations.collection_mut_risky(),
144            Collection::<R>::with_capacity(0),
145        );
146
147        let mut potential_relations = EntityHashSet::from_iter(related.iter().copied());
148
149        let id = self.id();
150        self.world_scope(|world| {
151            for related in existing_relations.iter() {
152                if !potential_relations.remove(related) {
153                    world.entity_mut(related).remove::<R>();
154                }
155            }
156
157            for related in potential_relations {
158                // SAFETY: We'll manually be adjusting the contents of the parent to fit the final state.
159                world
160                    .entity_mut(related)
161                    .insert_with_relationship_hook_mode(R::from(id), RelationshipHookMode::Skip);
162            }
163        });
164
165        // SAFETY: The entities we're inserting will be the entities that were either already there or entities that we've just inserted.
166        existing_relations.clear();
167        existing_relations.extend_from_iter(related.iter().copied());
168        self.insert(R::RelationshipTarget::from_collection_risky(
169            existing_relations,
170        ));
171
172        self
173    }
174
175    /// Replaces all the related entities with a new set of entities.
176    ///
177    /// This is a more efficient of [`Self::replace_related`] which doesn't allocate.
178    /// The passed in arguments must adhere to these invariants:
179    /// - `entities_to_unrelate`: A slice of entities to remove from the relationship source.
180    ///   Entities need not be related to this entity, but must not appear in `entities_to_relate`
181    /// - `entities_to_relate`: A slice of entities to relate to this entity.
182    ///   This must contain all entities that will remain related (i.e. not those in `entities_to_unrelate`) plus the newly related entities.
183    /// - `newly_related_entities`: A subset of `entities_to_relate` containing only entities not already related to this entity.
184    /// - Slices **must not** contain any duplicates
185    ///
186    /// # Warning
187    ///
188    /// Violating these invariants may lead to panics, crashes or unpredictable engine behavior.
189    ///
190    /// # Panics
191    ///
192    /// Panics when debug assertions are enabled and any invariants are broken.
193    ///
194    // TODO: Consider making these iterators so users aren't required to allocate a separate buffers for the different slices.
195    pub fn replace_related_with_difference<R: Relationship>(
196        &mut self,
197        entities_to_unrelate: &[Entity],
198        entities_to_relate: &[Entity],
199        newly_related_entities: &[Entity],
200    ) -> &mut Self {
201        #[cfg(debug_assertions)]
202        {
203            let entities_to_relate = EntityHashSet::from_iter(entities_to_relate.iter().copied());
204            let entities_to_unrelate =
205                EntityHashSet::from_iter(entities_to_unrelate.iter().copied());
206            let mut newly_related_entities =
207                EntityHashSet::from_iter(newly_related_entities.iter().copied());
208            assert!(
209                entities_to_relate.is_disjoint(&entities_to_unrelate),
210                "`entities_to_relate` ({entities_to_relate:?}) shared entities with `entities_to_unrelate` ({entities_to_unrelate:?})"
211            );
212            assert!(
213                newly_related_entities.is_disjoint(&entities_to_unrelate),
214                "`newly_related_entities` ({newly_related_entities:?}) shared entities with `entities_to_unrelate ({entities_to_unrelate:?})`"
215            );
216            assert!(
217                newly_related_entities.is_subset(&entities_to_relate),
218                "`newly_related_entities` ({newly_related_entities:?}) wasn't a subset of `entities_to_relate` ({entities_to_relate:?})"
219            );
220
221            if let Some(target) = self.get::<R::RelationshipTarget>() {
222                let existing_relationships: EntityHashSet = target.collection().iter().collect();
223
224                assert!(
225                    existing_relationships.is_disjoint(&newly_related_entities),
226                    "`newly_related_entities` contains an entity that wouldn't be newly related"
227                );
228
229                newly_related_entities.extend(existing_relationships);
230                newly_related_entities -= &entities_to_unrelate;
231            }
232
233            assert_eq!(newly_related_entities, entities_to_relate, "`entities_to_relate` ({entities_to_relate:?}) didn't contain all entities that would end up related");
234        };
235
236        if !self.contains::<R::RelationshipTarget>() {
237            self.add_related::<R>(entities_to_relate);
238
239            return self;
240        };
241
242        let this = self.id();
243        self.world_scope(|world| {
244            for unrelate in entities_to_unrelate {
245                world.entity_mut(*unrelate).remove::<R>();
246            }
247
248            for new_relation in newly_related_entities {
249                // We're changing the target collection manually so don't run the insert hook
250                world
251                    .entity_mut(*new_relation)
252                    .insert_with_relationship_hook_mode(R::from(this), RelationshipHookMode::Skip);
253            }
254        });
255
256        if !entities_to_relate.is_empty() {
257            if let Some(mut target) = self.get_mut::<R::RelationshipTarget>() {
258                // SAFETY: The invariants expected by this function mean we'll only be inserting entities that are already related.
259                let collection = target.collection_mut_risky();
260                collection.clear();
261
262                collection.extend_from_iter(entities_to_relate.iter().copied());
263            } else {
264                let mut empty =
265                    <R::RelationshipTarget as RelationshipTarget>::Collection::with_capacity(
266                        entities_to_relate.len(),
267                    );
268                empty.extend_from_iter(entities_to_relate.iter().copied());
269
270                // SAFETY: We've just initialized this collection and we know there's no `RelationshipTarget` on `self`
271                self.insert(R::RelationshipTarget::from_collection_risky(empty));
272            }
273        }
274
275        self
276    }
277
278    /// Relates the given entity to this with the relation `R`.
279    ///
280    /// See [`add_related`](Self::add_related) if you want to relate more than one entity.
281    pub fn add_one_related<R: Relationship>(&mut self, entity: Entity) -> &mut Self {
282        self.add_related::<R>(&[entity])
283    }
284
285    /// Despawns entities that relate to this one via the given [`RelationshipTarget`].
286    /// This entity will not be despawned.
287    pub fn despawn_related<S: RelationshipTarget>(&mut self) -> &mut Self {
288        if let Some(sources) = self.take::<S>() {
289            self.world_scope(|world| {
290                for entity in sources.iter() {
291                    if let Ok(entity_mut) = world.get_entity_mut(entity) {
292                        entity_mut.despawn();
293                    }
294                }
295            });
296        }
297        self
298    }
299
300    /// Inserts a component or bundle of components into the entity and all related entities,
301    /// traversing the relationship tracked in `S` in a breadth-first manner.
302    ///
303    /// # Warning
304    ///
305    /// This method should only be called on relationships that form a tree-like structure.
306    /// Any cycles will cause this method to loop infinitely.
307    // We could keep track of a list of visited entities and track cycles,
308    // but this is not a very well-defined operation (or hard to write) for arbitrary relationships.
309    pub fn insert_recursive<S: RelationshipTarget>(
310        &mut self,
311        bundle: impl Bundle + Clone,
312    ) -> &mut Self {
313        self.insert(bundle.clone());
314        if let Some(relationship_target) = self.get::<S>() {
315            let related_vec: Vec<Entity> = relationship_target.iter().collect();
316            for related in related_vec {
317                self.world_scope(|world| {
318                    world
319                        .entity_mut(related)
320                        .insert_recursive::<S>(bundle.clone());
321                });
322            }
323        }
324
325        self
326    }
327
328    /// Removes a component or bundle of components of type `B` from the entity and all related entities,
329    /// traversing the relationship tracked in `S` in a breadth-first manner.
330    ///
331    /// # Warning
332    ///
333    /// This method should only be called on relationships that form a tree-like structure.
334    /// Any cycles will cause this method to loop infinitely.
335    pub fn remove_recursive<S: RelationshipTarget, B: Bundle>(&mut self) -> &mut Self {
336        self.remove::<B>();
337        if let Some(relationship_target) = self.get::<S>() {
338            let related_vec: Vec<Entity> = relationship_target.iter().collect();
339            for related in related_vec {
340                self.world_scope(|world| {
341                    world.entity_mut(related).remove_recursive::<S, B>();
342                });
343            }
344        }
345
346        self
347    }
348}
349
350impl<'a> EntityCommands<'a> {
351    /// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle
352    pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {
353        let parent = self.id();
354        self.commands.spawn((bundle, R::from(parent)));
355        self
356    }
357
358    /// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].
359    pub fn with_related_entities<R: Relationship>(
360        &mut self,
361        func: impl FnOnce(&mut RelatedSpawnerCommands<R>),
362    ) -> &mut Self {
363        let id = self.id();
364        func(&mut RelatedSpawnerCommands::new(self.commands(), id));
365        self
366    }
367
368    /// Relates the given entities to this entity with the relation `R`.
369    ///
370    /// See [`add_one_related`](Self::add_one_related) if you want relate only one entity.
371    pub fn add_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
372        let related: Box<[Entity]> = related.into();
373
374        self.queue(move |mut entity: EntityWorldMut| {
375            entity.add_related::<R>(&related);
376        })
377    }
378
379    /// Relates the given entities to this entity with the relation `R`, starting at this particular index.
380    ///
381    /// If the `related` has duplicates, a related entity will take the index of its last occurrence in `related`.
382    /// If the indices go out of bounds, they will be clamped into bounds.
383    /// This will not re-order existing related entities unless they are in `related`.
384    pub fn insert_related<R: Relationship>(&mut self, index: usize, related: &[Entity]) -> &mut Self
385    where
386        <R::RelationshipTarget as RelationshipTarget>::Collection:
387            OrderedRelationshipSourceCollection,
388    {
389        let related: Box<[Entity]> = related.into();
390
391        self.queue(move |mut entity: EntityWorldMut| {
392            entity.insert_related::<R>(index, &related);
393        })
394    }
395
396    /// Relates the given entity to this with the relation `R`.
397    ///
398    /// See [`add_related`](Self::add_related) if you want to relate more than one entity.
399    pub fn add_one_related<R: Relationship>(&mut self, entity: Entity) -> &mut Self {
400        self.add_related::<R>(&[entity])
401    }
402
403    /// Removes the relation `R` between this entity and the given entities.
404    pub fn remove_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
405        let related: Box<[Entity]> = related.into();
406
407        self.queue(move |mut entity: EntityWorldMut| {
408            entity.remove_related::<R>(&related);
409        })
410    }
411
412    /// Replaces all the related entities with the given set of new related entities.
413    pub fn replace_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
414        let related: Box<[Entity]> = related.into();
415
416        self.queue(move |mut entity: EntityWorldMut| {
417            entity.replace_related::<R>(&related);
418        })
419    }
420
421    /// Replaces all the related entities with a new set of entities.
422    ///
423    /// # Warning
424    ///
425    /// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
426    /// Refer to [`EntityWorldMut::replace_related_with_difference`] for a list of these invariants.
427    ///
428    /// # Panics
429    ///
430    /// Panics when debug assertions are enable, an invariant is are broken and the command is executed.
431    pub fn replace_related_with_difference<R: Relationship>(
432        &mut self,
433        entities_to_unrelate: &[Entity],
434        entities_to_relate: &[Entity],
435        newly_related_entities: &[Entity],
436    ) -> &mut Self {
437        let entities_to_unrelate: Box<[Entity]> = entities_to_unrelate.into();
438        let entities_to_relate: Box<[Entity]> = entities_to_relate.into();
439        let newly_related_entities: Box<[Entity]> = newly_related_entities.into();
440
441        self.queue(move |mut entity: EntityWorldMut| {
442            entity.replace_related_with_difference::<R>(
443                &entities_to_unrelate,
444                &entities_to_relate,
445                &newly_related_entities,
446            );
447        })
448    }
449
450    /// Despawns entities that relate to this one via the given [`RelationshipTarget`].
451    /// This entity will not be despawned.
452    pub fn despawn_related<S: RelationshipTarget>(&mut self) -> &mut Self {
453        self.queue(move |mut entity: EntityWorldMut| {
454            entity.despawn_related::<S>();
455        })
456    }
457
458    /// Inserts a component or bundle of components into the entity and all related entities,
459    /// traversing the relationship tracked in `S` in a breadth-first manner.
460    ///
461    /// # Warning
462    ///
463    /// This method should only be called on relationships that form a tree-like structure.
464    /// Any cycles will cause this method to loop infinitely.
465    pub fn insert_recursive<S: RelationshipTarget>(
466        &mut self,
467        bundle: impl Bundle + Clone,
468    ) -> &mut Self {
469        self.queue(move |mut entity: EntityWorldMut| {
470            entity.insert_recursive::<S>(bundle);
471        })
472    }
473
474    /// Removes a component or bundle of components of type `B` from the entity and all related entities,
475    /// traversing the relationship tracked in `S` in a breadth-first manner.
476    ///
477    /// # Warning
478    ///
479    /// This method should only be called on relationships that form a tree-like structure.
480    /// Any cycles will cause this method to loop infinitely.
481    pub fn remove_recursive<S: RelationshipTarget, B: Bundle>(&mut self) -> &mut Self {
482        self.queue(move |mut entity: EntityWorldMut| {
483            entity.remove_recursive::<S, B>();
484        })
485    }
486}
487
488/// Directly spawns related "source" entities with the given [`Relationship`], targeting
489/// a specific entity.
490pub struct RelatedSpawner<'w, R: Relationship> {
491    target: Entity,
492    world: &'w mut World,
493    _marker: PhantomData<R>,
494}
495
496impl<'w, R: Relationship> RelatedSpawner<'w, R> {
497    /// Creates a new instance that will spawn entities targeting the `target` entity.
498    pub fn new(world: &'w mut World, target: Entity) -> Self {
499        Self {
500            world,
501            target,
502            _marker: PhantomData,
503        }
504    }
505
506    /// Spawns an entity with the given `bundle` and an `R` relationship targeting the `target`
507    /// entity this spawner was initialized with.
508    pub fn spawn(&mut self, bundle: impl Bundle) -> EntityWorldMut<'_> {
509        self.world.spawn((R::from(self.target), bundle))
510    }
511
512    /// Spawns an entity with an `R` relationship targeting the `target`
513    /// entity this spawner was initialized with.
514    pub fn spawn_empty(&mut self) -> EntityWorldMut<'_> {
515        self.world.spawn(R::from(self.target))
516    }
517
518    /// Returns the "target entity" used when spawning entities with an `R` [`Relationship`].
519    pub fn target_entity(&self) -> Entity {
520        self.target
521    }
522}
523
524/// Uses commands to spawn related "source" entities with the given [`Relationship`], targeting
525/// a specific entity.
526pub struct RelatedSpawnerCommands<'w, R: Relationship> {
527    target: Entity,
528    commands: Commands<'w, 'w>,
529    _marker: PhantomData<R>,
530}
531
532impl<'w, R: Relationship> RelatedSpawnerCommands<'w, R> {
533    /// Creates a new instance that will spawn entities targeting the `target` entity.
534    pub fn new(commands: Commands<'w, 'w>, target: Entity) -> Self {
535        Self {
536            commands,
537            target,
538            _marker: PhantomData,
539        }
540    }
541
542    /// Spawns an entity with the given `bundle` and an `R` relationship targeting the `target`
543    /// entity this spawner was initialized with.
544    pub fn spawn(&mut self, bundle: impl Bundle) -> EntityCommands<'_> {
545        self.commands.spawn((R::from(self.target), bundle))
546    }
547
548    /// Spawns an entity with an `R` relationship targeting the `target`
549    /// entity this spawner was initialized with.
550    pub fn spawn_empty(&mut self) -> EntityCommands<'_> {
551        self.commands.spawn(R::from(self.target))
552    }
553
554    /// Returns the "target entity" used when spawning entities with an `R` [`Relationship`].
555    pub fn target_entity(&self) -> Entity {
556        self.target
557    }
558
559    /// Returns the underlying [`Commands`].
560    pub fn commands(&mut self) -> Commands {
561        self.commands.reborrow()
562    }
563
564    /// Returns a mutable reference to the underlying [`Commands`].
565    pub fn commands_mut(&mut self) -> &mut Commands<'w, 'w> {
566        &mut self.commands
567    }
568}
569
570#[cfg(test)]
571mod tests {
572    use super::*;
573    use crate::prelude::{ChildOf, Children, Component};
574
575    #[derive(Component, Clone, Copy)]
576    struct TestComponent;
577
578    #[test]
579    fn insert_and_remove_recursive() {
580        let mut world = World::new();
581
582        let a = world.spawn_empty().id();
583        let b = world.spawn(ChildOf(a)).id();
584        let c = world.spawn(ChildOf(a)).id();
585        let d = world.spawn(ChildOf(b)).id();
586
587        world
588            .entity_mut(a)
589            .insert_recursive::<Children>(TestComponent);
590
591        for entity in [a, b, c, d] {
592            assert!(world.entity(entity).contains::<TestComponent>());
593        }
594
595        world
596            .entity_mut(b)
597            .remove_recursive::<Children, TestComponent>();
598
599        // Parent
600        assert!(world.entity(a).contains::<TestComponent>());
601        // Target
602        assert!(!world.entity(b).contains::<TestComponent>());
603        // Sibling
604        assert!(world.entity(c).contains::<TestComponent>());
605        // Child
606        assert!(!world.entity(d).contains::<TestComponent>());
607
608        world
609            .entity_mut(a)
610            .remove_recursive::<Children, TestComponent>();
611
612        for entity in [a, b, c, d] {
613            assert!(!world.entity(entity).contains::<TestComponent>());
614        }
615    }
616}