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}