bevy_ecs/relationship/
mod.rs

1//! This module provides functionality to link entities to each other using specialized components called "relationships". See the [`Relationship`] trait for more info.
2
3mod related_methods;
4mod relationship_query;
5mod relationship_source_collection;
6
7use alloc::format;
8
9pub use related_methods::*;
10pub use relationship_query::*;
11pub use relationship_source_collection::*;
12
13use crate::{
14    component::{Component, HookContext, Mutable},
15    entity::{ComponentCloneCtx, Entity, SourceComponent},
16    error::{ignore, CommandWithEntity, HandleError},
17    system::entity_command::{self},
18    world::{DeferredWorld, EntityWorldMut},
19};
20use log::warn;
21
22/// A [`Component`] on a "source" [`Entity`] that references another target [`Entity`], creating a "relationship" between them. Every [`Relationship`]
23/// has a corresponding [`RelationshipTarget`] type (and vice-versa), which exists on the "target" entity of a relationship and contains the list of all
24/// "source" entities that relate to the given "target"
25///
26/// The [`Relationship`] component is the "source of truth" and the [`RelationshipTarget`] component reflects that source of truth. When a [`Relationship`]
27/// component is inserted on an [`Entity`], the corresponding [`RelationshipTarget`] component is immediately inserted on the target component if it does
28/// not already exist, and the "source" entity is automatically added to the [`RelationshipTarget`] collection (this is done via "component hooks").
29///
30/// A common example of a [`Relationship`] is the parent / child relationship. Bevy ECS includes a canonical form of this via the [`ChildOf`](crate::hierarchy::ChildOf)
31/// [`Relationship`] and the [`Children`](crate::hierarchy::Children) [`RelationshipTarget`].
32///
33/// [`Relationship`] and [`RelationshipTarget`] should always be derived via the [`Component`] trait to ensure the hooks are set up properly.
34///
35/// ## Derive
36///
37/// [`Relationship`] and [`RelationshipTarget`] can only be derived for structs with a single unnamed field, single named field
38/// or for named structs where one field is annotated with `#[relationship]`.
39/// If there are additional fields, they must all implement [`Default`].
40///
41/// [`RelationshipTarget`] also requires that the relationship field is private to prevent direct mutation,
42/// ensuring the correctness of relationships.
43/// ```
44/// # use bevy_ecs::component::Component;
45/// # use bevy_ecs::entity::Entity;
46/// #[derive(Component)]
47/// #[relationship(relationship_target = Children)]
48/// pub struct ChildOf {
49///     #[relationship]
50///     pub parent: Entity,
51///     internal: u8,
52/// };
53///
54/// #[derive(Component)]
55/// #[relationship_target(relationship = ChildOf)]
56/// pub struct Children(Vec<Entity>);
57/// ```
58///
59/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(linked_spawn)]` attribute to
60/// automatically despawn entities stored in an entity's [`RelationshipTarget`] when that entity is despawned:
61///
62/// ```
63/// # use bevy_ecs::component::Component;
64/// # use bevy_ecs::entity::Entity;
65/// #[derive(Component)]
66/// #[relationship(relationship_target = Children)]
67/// pub struct ChildOf(pub Entity);
68///
69/// #[derive(Component)]
70/// #[relationship_target(relationship = ChildOf, linked_spawn)]
71/// pub struct Children(Vec<Entity>);
72/// ```
73pub trait Relationship: Component + Sized {
74    /// The [`Component`] added to the "target" entities of this [`Relationship`], which contains the list of all "source"
75    /// entities that relate to the "target".
76    type RelationshipTarget: RelationshipTarget<Relationship = Self>;
77
78    /// Gets the [`Entity`] ID of the related entity.
79    fn get(&self) -> Entity;
80
81    /// Creates this [`Relationship`] from the given `entity`.
82    fn from(entity: Entity) -> Self;
83
84    /// The `on_insert` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
85    fn on_insert(
86        mut world: DeferredWorld,
87        HookContext {
88            entity,
89            caller,
90            relationship_hook_mode,
91            ..
92        }: HookContext,
93    ) {
94        match relationship_hook_mode {
95            RelationshipHookMode::Run => {}
96            RelationshipHookMode::Skip => return,
97            RelationshipHookMode::RunIfNotLinked => {
98                if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
99                    return;
100                }
101            }
102        }
103        let target_entity = world.entity(entity).get::<Self>().unwrap().get();
104        if target_entity == entity {
105            warn!(
106                "{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.",
107                caller.map(|location|format!("{location}: ")).unwrap_or_default(),
108                core::any::type_name::<Self>(),
109                core::any::type_name::<Self>()
110            );
111            world.commands().entity(entity).remove::<Self>();
112            return;
113        }
114        if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) {
115            if let Some(mut relationship_target) =
116                target_entity_mut.get_mut::<Self::RelationshipTarget>()
117            {
118                relationship_target.collection_mut_risky().add(entity);
119            } else {
120                let mut target = <Self::RelationshipTarget as RelationshipTarget>::with_capacity(1);
121                target.collection_mut_risky().add(entity);
122                world.commands().entity(target_entity).insert(target);
123            }
124        } else {
125            warn!(
126                "{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.",
127                caller.map(|location|format!("{location}: ")).unwrap_or_default(),
128                core::any::type_name::<Self>(),
129                core::any::type_name::<Self>()
130            );
131            world.commands().entity(entity).remove::<Self>();
132        }
133    }
134
135    /// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
136    // note: think of this as "on_drop"
137    fn on_replace(
138        mut world: DeferredWorld,
139        HookContext {
140            entity,
141            relationship_hook_mode,
142            ..
143        }: HookContext,
144    ) {
145        match relationship_hook_mode {
146            RelationshipHookMode::Run => {}
147            RelationshipHookMode::Skip => return,
148            RelationshipHookMode::RunIfNotLinked => {
149                if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
150                    return;
151                }
152            }
153        }
154        let target_entity = world.entity(entity).get::<Self>().unwrap().get();
155        if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) {
156            if let Some(mut relationship_target) =
157                target_entity_mut.get_mut::<Self::RelationshipTarget>()
158            {
159                relationship_target.collection_mut_risky().remove(entity);
160                if relationship_target.len() == 0 {
161                    let command = |mut entity: EntityWorldMut| {
162                        // this "remove" operation must check emptiness because in the event that an identical
163                        // relationship is inserted on top, this despawn would result in the removal of that identical
164                        // relationship ... not what we want!
165                        if entity
166                            .get::<Self::RelationshipTarget>()
167                            .is_some_and(RelationshipTarget::is_empty)
168                        {
169                            entity.remove::<Self::RelationshipTarget>();
170                        }
171                    };
172
173                    world
174                        .commands()
175                        .queue(command.with_entity(target_entity).handle_error_with(ignore));
176                }
177            }
178        }
179    }
180}
181
182/// The iterator type for the source entities in a [`RelationshipTarget`] collection,
183/// as defined in the [`RelationshipSourceCollection`] trait.
184pub type SourceIter<'w, R> =
185    <<R as RelationshipTarget>::Collection as RelationshipSourceCollection>::SourceIter<'w>;
186
187/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type.
188/// See the [`Relationship`] documentation for more information.
189pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
190    /// If this is true, when despawning or cloning (when [linked cloning is enabled](crate::entity::EntityClonerBuilder::linked_cloning)), the related entities targeting this entity will also be despawned or cloned.
191    ///
192    /// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children).
193    /// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning).
194    ///
195    /// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone.
196    /// This defaults to false when derived.
197    const LINKED_SPAWN: bool;
198    /// The [`Relationship`] that populates this [`RelationshipTarget`] collection.
199    type Relationship: Relationship<RelationshipTarget = Self>;
200    /// The collection type that stores the "source" entities for this [`RelationshipTarget`] component.
201    ///
202    /// Check the list of types which implement [`RelationshipSourceCollection`] for the data structures that can be used inside of your component.
203    /// If you need a new collection type, you can implement the [`RelationshipSourceCollection`] trait
204    /// for a type you own which wraps the collection you want to use (to avoid the orphan rule),
205    /// or open an issue on the Bevy repository to request first-party support for your collection type.
206    type Collection: RelationshipSourceCollection;
207
208    /// Returns a reference to the stored [`RelationshipTarget::Collection`].
209    fn collection(&self) -> &Self::Collection;
210    /// Returns a mutable reference to the stored [`RelationshipTarget::Collection`].
211    ///
212    /// # Warning
213    /// This should generally not be called by user code, as modifying the internal collection could invalidate the relationship.
214    /// The collection should not contain duplicates.
215    fn collection_mut_risky(&mut self) -> &mut Self::Collection;
216
217    /// Creates a new [`RelationshipTarget`] from the given [`RelationshipTarget::Collection`].
218    ///
219    /// # Warning
220    /// This should generally not be called by user code, as constructing the internal collection could invalidate the relationship.
221    /// The collection should not contain duplicates.
222    fn from_collection_risky(collection: Self::Collection) -> Self;
223
224    /// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
225    // note: think of this as "on_drop"
226    fn on_replace(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
227        let (entities, mut commands) = world.entities_and_commands();
228        let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
229        for source_entity in relationship_target.iter() {
230            if entities.get(source_entity).is_ok() {
231                commands.queue(
232                    entity_command::remove::<Self::Relationship>()
233                        .with_entity(source_entity)
234                        .handle_error_with(ignore),
235                );
236            } else {
237                warn!(
238                    "{}Tried to despawn non-existent entity {}",
239                    caller
240                        .map(|location| format!("{location}: "))
241                        .unwrap_or_default(),
242                    source_entity
243                );
244            }
245        }
246    }
247
248    /// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when
249    /// that entity is despawned.
250    // note: think of this as "on_drop"
251    fn on_despawn(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
252        let (entities, mut commands) = world.entities_and_commands();
253        let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
254        for source_entity in relationship_target.iter() {
255            if entities.get(source_entity).is_ok() {
256                commands.queue(
257                    entity_command::despawn()
258                        .with_entity(source_entity)
259                        .handle_error_with(ignore),
260                );
261            } else {
262                warn!(
263                    "{}Tried to despawn non-existent entity {}",
264                    caller
265                        .map(|location| format!("{location}: "))
266                        .unwrap_or_default(),
267                    source_entity
268                );
269            }
270        }
271    }
272
273    /// Creates this [`RelationshipTarget`] with the given pre-allocated entity capacity.
274    fn with_capacity(capacity: usize) -> Self {
275        let collection =
276            <Self::Collection as RelationshipSourceCollection>::with_capacity(capacity);
277        Self::from_collection_risky(collection)
278    }
279
280    /// Iterates the entities stored in this collection.
281    #[inline]
282    fn iter(&self) -> SourceIter<'_, Self> {
283        self.collection().iter()
284    }
285
286    /// Returns the number of entities in this collection.
287    #[inline]
288    fn len(&self) -> usize {
289        self.collection().len()
290    }
291
292    /// Returns true if this entity collection is empty.
293    #[inline]
294    fn is_empty(&self) -> bool {
295        self.collection().is_empty()
296    }
297}
298
299/// The "clone behavior" for [`RelationshipTarget`]. This actually creates an empty
300/// [`RelationshipTarget`] instance with space reserved for the number of targets in the
301/// original instance. The [`RelationshipTarget`] will then be populated with the proper components
302/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities
303/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that!
304///
305/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured
306/// to spawn recursively.
307pub fn clone_relationship_target<T: RelationshipTarget>(
308    source: &SourceComponent,
309    context: &mut ComponentCloneCtx,
310) {
311    if let Some(component) = source.read::<T>() {
312        let mut cloned = T::with_capacity(component.len());
313        if context.linked_cloning() && T::LINKED_SPAWN {
314            let collection = cloned.collection_mut_risky();
315            for entity in component.iter() {
316                collection.add(entity);
317                context.queue_entity_clone(entity);
318            }
319        }
320        context.write_target_component(cloned);
321    }
322}
323
324/// Configures the conditions under which the Relationship insert/replace hooks will be run.
325#[derive(Copy, Clone, Debug)]
326pub enum RelationshipHookMode {
327    /// Relationship insert/replace hooks will always run
328    Run,
329    /// Relationship insert/replace hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false
330    RunIfNotLinked,
331    /// Relationship insert/replace hooks will always be skipped
332    Skip,
333}
334
335#[cfg(test)]
336mod tests {
337    use crate::world::World;
338    use crate::{component::Component, entity::Entity};
339    use alloc::vec::Vec;
340
341    #[test]
342    fn custom_relationship() {
343        #[derive(Component)]
344        #[relationship(relationship_target = LikedBy)]
345        struct Likes(pub Entity);
346
347        #[derive(Component)]
348        #[relationship_target(relationship = Likes)]
349        struct LikedBy(Vec<Entity>);
350
351        let mut world = World::new();
352        let a = world.spawn_empty().id();
353        let b = world.spawn(Likes(a)).id();
354        let c = world.spawn(Likes(a)).id();
355        assert_eq!(world.entity(a).get::<LikedBy>().unwrap().0, &[b, c]);
356    }
357
358    #[test]
359    fn self_relationship_fails() {
360        #[derive(Component)]
361        #[relationship(relationship_target = RelTarget)]
362        struct Rel(Entity);
363
364        #[derive(Component)]
365        #[relationship_target(relationship = Rel)]
366        struct RelTarget(Vec<Entity>);
367
368        let mut world = World::new();
369        let a = world.spawn_empty().id();
370        world.entity_mut(a).insert(Rel(a));
371        assert!(!world.entity(a).contains::<Rel>());
372        assert!(!world.entity(a).contains::<RelTarget>());
373    }
374
375    #[test]
376    fn relationship_with_missing_target_fails() {
377        #[derive(Component)]
378        #[relationship(relationship_target = RelTarget)]
379        struct Rel(Entity);
380
381        #[derive(Component)]
382        #[relationship_target(relationship = Rel)]
383        struct RelTarget(Vec<Entity>);
384
385        let mut world = World::new();
386        let a = world.spawn_empty().id();
387        world.despawn(a);
388        let b = world.spawn(Rel(a)).id();
389        assert!(!world.entity(b).contains::<Rel>());
390        assert!(!world.entity(b).contains::<RelTarget>());
391    }
392
393    #[test]
394    fn relationship_with_multiple_non_target_fields_compiles() {
395        #[derive(Component)]
396        #[relationship(relationship_target=Target)]
397        #[expect(dead_code, reason = "test struct")]
398        struct Source {
399            #[relationship]
400            target: Entity,
401            foo: u8,
402            bar: u8,
403        }
404
405        #[derive(Component)]
406        #[relationship_target(relationship=Source)]
407        struct Target(Vec<Entity>);
408
409        // No assert necessary, looking to make sure compilation works with the macros
410    }
411    #[test]
412    fn relationship_target_with_multiple_non_target_fields_compiles() {
413        #[derive(Component)]
414        #[relationship(relationship_target=Target)]
415        struct Source(Entity);
416
417        #[derive(Component)]
418        #[relationship_target(relationship=Source)]
419        #[expect(dead_code, reason = "test struct")]
420        struct Target {
421            #[relationship]
422            target: Vec<Entity>,
423            foo: u8,
424            bar: u8,
425        }
426
427        // No assert necessary, looking to make sure compilation works with the macros
428    }
429
430    #[test]
431    fn parent_child_relationship_with_custom_relationship() {
432        use crate::prelude::ChildOf;
433
434        #[derive(Component)]
435        #[relationship(relationship_target = RelTarget)]
436        struct Rel(Entity);
437
438        #[derive(Component)]
439        #[relationship_target(relationship = Rel)]
440        struct RelTarget(Entity);
441
442        let mut world = World::new();
443
444        // Rel on Parent
445        // Despawn Parent
446        let mut commands = world.commands();
447        let child = commands.spawn_empty().id();
448        let parent = commands.spawn(Rel(child)).add_child(child).id();
449        commands.entity(parent).despawn();
450        world.flush();
451
452        assert!(world.get_entity(child).is_err());
453        assert!(world.get_entity(parent).is_err());
454
455        // Rel on Parent
456        // Despawn Child
457        let mut commands = world.commands();
458        let child = commands.spawn_empty().id();
459        let parent = commands.spawn(Rel(child)).add_child(child).id();
460        commands.entity(child).despawn();
461        world.flush();
462
463        assert!(world.get_entity(child).is_err());
464        assert!(!world.entity(parent).contains::<Rel>());
465
466        // Rel on Child
467        // Despawn Parent
468        let mut commands = world.commands();
469        let parent = commands.spawn_empty().id();
470        let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
471        commands.entity(parent).despawn();
472        world.flush();
473
474        assert!(world.get_entity(child).is_err());
475        assert!(world.get_entity(parent).is_err());
476
477        // Rel on Child
478        // Despawn Child
479        let mut commands = world.commands();
480        let parent = commands.spawn_empty().id();
481        let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
482        commands.entity(child).despawn();
483        world.flush();
484
485        assert!(world.get_entity(child).is_err());
486        assert!(!world.entity(parent).contains::<RelTarget>());
487    }
488}