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}