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                    if let Ok(mut entity) = world.commands().get_entity(target_entity) {
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                        entity.queue(|mut entity: EntityWorldMut| {
166                            if entity
167                                .get::<Self::RelationshipTarget>()
168                                .is_some_and(RelationshipTarget::is_empty)
169                            {
170                                entity.remove::<Self::RelationshipTarget>();
171                            }
172                        });
173                    }
174                }
175            }
176        }
177    }
178}
179
180/// The iterator type for the source entities in a [`RelationshipTarget`] collection,
181/// as defined in the [`RelationshipSourceCollection`] trait.
182pub type SourceIter<'w, R> =
183    <<R as RelationshipTarget>::Collection as RelationshipSourceCollection>::SourceIter<'w>;
184
185/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type.
186/// See the [`Relationship`] documentation for more information.
187pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
188    /// 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.
189    ///
190    /// 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).
191    /// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning).
192    ///
193    /// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone.
194    /// This defaults to false when derived.
195    const LINKED_SPAWN: bool;
196    /// The [`Relationship`] that populates this [`RelationshipTarget`] collection.
197    type Relationship: Relationship<RelationshipTarget = Self>;
198    /// The collection type that stores the "source" entities for this [`RelationshipTarget`] component.
199    ///
200    /// Check the list of types which implement [`RelationshipSourceCollection`] for the data structures that can be used inside of your component.
201    /// If you need a new collection type, you can implement the [`RelationshipSourceCollection`] trait
202    /// for a type you own which wraps the collection you want to use (to avoid the orphan rule),
203    /// or open an issue on the Bevy repository to request first-party support for your collection type.
204    type Collection: RelationshipSourceCollection;
205
206    /// Returns a reference to the stored [`RelationshipTarget::Collection`].
207    fn collection(&self) -> &Self::Collection;
208    /// Returns a mutable reference to the stored [`RelationshipTarget::Collection`].
209    ///
210    /// # Warning
211    /// This should generally not be called by user code, as modifying the internal collection could invalidate the relationship.
212    /// The collection should not contain duplicates.
213    fn collection_mut_risky(&mut self) -> &mut Self::Collection;
214
215    /// Creates a new [`RelationshipTarget`] from the given [`RelationshipTarget::Collection`].
216    ///
217    /// # Warning
218    /// This should generally not be called by user code, as constructing the internal collection could invalidate the relationship.
219    /// The collection should not contain duplicates.
220    fn from_collection_risky(collection: Self::Collection) -> Self;
221
222    /// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
223    // note: think of this as "on_drop"
224    fn on_replace(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
225        let (entities, mut commands) = world.entities_and_commands();
226        let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
227        for source_entity in relationship_target.iter() {
228            if entities.get(source_entity).is_ok() {
229                commands.queue(
230                    entity_command::remove::<Self::Relationship>()
231                        .with_entity(source_entity)
232                        .handle_error_with(ignore),
233                );
234            } else {
235                warn!(
236                    "{}Tried to despawn non-existent entity {}",
237                    caller
238                        .map(|location| format!("{location}: "))
239                        .unwrap_or_default(),
240                    source_entity
241                );
242            }
243        }
244    }
245
246    /// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when
247    /// that entity is despawned.
248    // note: think of this as "on_drop"
249    fn on_despawn(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
250        let (entities, mut commands) = world.entities_and_commands();
251        let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
252        for source_entity in relationship_target.iter() {
253            if entities.get(source_entity).is_ok() {
254                commands.queue(
255                    entity_command::despawn()
256                        .with_entity(source_entity)
257                        .handle_error_with(ignore),
258                );
259            } else {
260                warn!(
261                    "{}Tried to despawn non-existent entity {}",
262                    caller
263                        .map(|location| format!("{location}: "))
264                        .unwrap_or_default(),
265                    source_entity
266                );
267            }
268        }
269    }
270
271    /// Creates this [`RelationshipTarget`] with the given pre-allocated entity capacity.
272    fn with_capacity(capacity: usize) -> Self {
273        let collection =
274            <Self::Collection as RelationshipSourceCollection>::with_capacity(capacity);
275        Self::from_collection_risky(collection)
276    }
277
278    /// Iterates the entities stored in this collection.
279    #[inline]
280    fn iter(&self) -> SourceIter<'_, Self> {
281        self.collection().iter()
282    }
283
284    /// Returns the number of entities in this collection.
285    #[inline]
286    fn len(&self) -> usize {
287        self.collection().len()
288    }
289
290    /// Returns true if this entity collection is empty.
291    #[inline]
292    fn is_empty(&self) -> bool {
293        self.collection().is_empty()
294    }
295}
296
297/// The "clone behavior" for [`RelationshipTarget`]. This actually creates an empty
298/// [`RelationshipTarget`] instance with space reserved for the number of targets in the
299/// original instance. The [`RelationshipTarget`] will then be populated with the proper components
300/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities
301/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that!
302///
303/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured
304/// to spawn recursively.
305pub fn clone_relationship_target<T: RelationshipTarget>(
306    source: &SourceComponent,
307    context: &mut ComponentCloneCtx,
308) {
309    if let Some(component) = source.read::<T>() {
310        let mut cloned = T::with_capacity(component.len());
311        if context.linked_cloning() && T::LINKED_SPAWN {
312            let collection = cloned.collection_mut_risky();
313            for entity in component.iter() {
314                collection.add(entity);
315                context.queue_entity_clone(entity);
316            }
317        }
318        context.write_target_component(cloned);
319    }
320}
321
322/// Configures the conditions under which the Relationship insert/replace hooks will be run.
323#[derive(Copy, Clone, Debug)]
324pub enum RelationshipHookMode {
325    /// Relationship insert/replace hooks will always run
326    Run,
327    /// Relationship insert/replace hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false
328    RunIfNotLinked,
329    /// Relationship insert/replace hooks will always be skipped
330    Skip,
331}
332
333#[cfg(test)]
334mod tests {
335    use crate::world::World;
336    use crate::{component::Component, entity::Entity};
337    use alloc::vec::Vec;
338
339    #[test]
340    fn custom_relationship() {
341        #[derive(Component)]
342        #[relationship(relationship_target = LikedBy)]
343        struct Likes(pub Entity);
344
345        #[derive(Component)]
346        #[relationship_target(relationship = Likes)]
347        struct LikedBy(Vec<Entity>);
348
349        let mut world = World::new();
350        let a = world.spawn_empty().id();
351        let b = world.spawn(Likes(a)).id();
352        let c = world.spawn(Likes(a)).id();
353        assert_eq!(world.entity(a).get::<LikedBy>().unwrap().0, &[b, c]);
354    }
355
356    #[test]
357    fn self_relationship_fails() {
358        #[derive(Component)]
359        #[relationship(relationship_target = RelTarget)]
360        struct Rel(Entity);
361
362        #[derive(Component)]
363        #[relationship_target(relationship = Rel)]
364        struct RelTarget(Vec<Entity>);
365
366        let mut world = World::new();
367        let a = world.spawn_empty().id();
368        world.entity_mut(a).insert(Rel(a));
369        assert!(!world.entity(a).contains::<Rel>());
370        assert!(!world.entity(a).contains::<RelTarget>());
371    }
372
373    #[test]
374    fn relationship_with_missing_target_fails() {
375        #[derive(Component)]
376        #[relationship(relationship_target = RelTarget)]
377        struct Rel(Entity);
378
379        #[derive(Component)]
380        #[relationship_target(relationship = Rel)]
381        struct RelTarget(Vec<Entity>);
382
383        let mut world = World::new();
384        let a = world.spawn_empty().id();
385        world.despawn(a);
386        let b = world.spawn(Rel(a)).id();
387        assert!(!world.entity(b).contains::<Rel>());
388        assert!(!world.entity(b).contains::<RelTarget>());
389    }
390
391    #[test]
392    fn relationship_with_multiple_non_target_fields_compiles() {
393        #[derive(Component)]
394        #[relationship(relationship_target=Target)]
395        #[expect(dead_code, reason = "test struct")]
396        struct Source {
397            #[relationship]
398            target: Entity,
399            foo: u8,
400            bar: u8,
401        }
402
403        #[derive(Component)]
404        #[relationship_target(relationship=Source)]
405        struct Target(Vec<Entity>);
406
407        // No assert necessary, looking to make sure compilation works with the macros
408    }
409    #[test]
410    fn relationship_target_with_multiple_non_target_fields_compiles() {
411        #[derive(Component)]
412        #[relationship(relationship_target=Target)]
413        struct Source(Entity);
414
415        #[derive(Component)]
416        #[relationship_target(relationship=Source)]
417        #[expect(dead_code, reason = "test struct")]
418        struct Target {
419            #[relationship]
420            target: Vec<Entity>,
421            foo: u8,
422            bar: u8,
423        }
424
425        // No assert necessary, looking to make sure compilation works with the macros
426    }
427}