Path: blob/main/crates/bevy_ecs/src/relationship/mod.rs
6600 views
//! This module provides functionality to link entities to each other using specialized components called "relationships". See the [`Relationship`] trait for more info.12mod related_methods;3mod relationship_query;4mod relationship_source_collection;56use core::marker::PhantomData;78use alloc::format;910use bevy_utils::prelude::DebugName;11pub use related_methods::*;12pub use relationship_query::*;13pub use relationship_source_collection::*;1415use crate::{16component::{Component, ComponentCloneBehavior, Mutable},17entity::{ComponentCloneCtx, Entity},18error::CommandWithEntity,19lifecycle::HookContext,20world::{DeferredWorld, EntityWorldMut},21};22use log::warn;2324/// A [`Component`] on a "source" [`Entity`] that references another target [`Entity`], creating a "relationship" between them. Every [`Relationship`]25/// has a corresponding [`RelationshipTarget`] type (and vice-versa), which exists on the "target" entity of a relationship and contains the list of all26/// "source" entities that relate to the given "target"27///28/// The [`Relationship`] component is the "source of truth" and the [`RelationshipTarget`] component reflects that source of truth. When a [`Relationship`]29/// component is inserted on an [`Entity`], the corresponding [`RelationshipTarget`] component is immediately inserted on the target component if it does30/// not already exist, and the "source" entity is automatically added to the [`RelationshipTarget`] collection (this is done via "component hooks").31///32/// 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)33/// [`Relationship`] and the [`Children`](crate::hierarchy::Children) [`RelationshipTarget`].34///35/// [`Relationship`] and [`RelationshipTarget`] should always be derived via the [`Component`] trait to ensure the hooks are set up properly.36///37/// ## Derive38///39/// [`Relationship`] and [`RelationshipTarget`] can only be derived for structs with a single unnamed field, single named field40/// or for named structs where one field is annotated with `#[relationship]`.41/// If there are additional fields, they must all implement [`Default`].42///43/// [`RelationshipTarget`] also requires that the relationship field is private to prevent direct mutation,44/// ensuring the correctness of relationships.45/// ```46/// # use bevy_ecs::component::Component;47/// # use bevy_ecs::entity::Entity;48/// #[derive(Component)]49/// #[relationship(relationship_target = Children)]50/// pub struct ChildOf {51/// #[relationship]52/// pub parent: Entity,53/// internal: u8,54/// };55///56/// #[derive(Component)]57/// #[relationship_target(relationship = ChildOf)]58/// pub struct Children(Vec<Entity>);59/// ```60///61/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(linked_spawn)]` attribute to62/// automatically despawn entities stored in an entity's [`RelationshipTarget`] when that entity is despawned:63///64/// ```65/// # use bevy_ecs::component::Component;66/// # use bevy_ecs::entity::Entity;67/// #[derive(Component)]68/// #[relationship(relationship_target = Children)]69/// pub struct ChildOf(pub Entity);70///71/// #[derive(Component)]72/// #[relationship_target(relationship = ChildOf, linked_spawn)]73/// pub struct Children(Vec<Entity>);74/// ```75pub trait Relationship: Component + Sized {76/// The [`Component`] added to the "target" entities of this [`Relationship`], which contains the list of all "source"77/// entities that relate to the "target".78type RelationshipTarget: RelationshipTarget<Relationship = Self>;7980/// Gets the [`Entity`] ID of the related entity.81fn get(&self) -> Entity;8283/// Creates this [`Relationship`] from the given `entity`.84fn from(entity: Entity) -> Self;8586/// Changes the current [`Entity`] ID of the entity containing the [`RelationshipTarget`] to another one.87///88/// This is useful for updating the relationship without overwriting other fields stored in `Self`.89///90/// # Warning91///92/// This should generally not be called by user code, as modifying the related entity could invalidate the93/// relationship. If this method is used, then the hooks [`on_replace`](Relationship::on_replace) have to94/// run before and [`on_insert`](Relationship::on_insert) after it.95/// This happens automatically when this method is called with [`EntityWorldMut::modify_component`].96///97/// Prefer to use regular means of insertions when possible.98fn set_risky(&mut self, entity: Entity);99100/// The `on_insert` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.101fn on_insert(102mut world: DeferredWorld,103HookContext {104entity,105caller,106relationship_hook_mode,107..108}: HookContext,109) {110match relationship_hook_mode {111RelationshipHookMode::Run => {}112RelationshipHookMode::Skip => return,113RelationshipHookMode::RunIfNotLinked => {114if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {115return;116}117}118}119let target_entity = world.entity(entity).get::<Self>().unwrap().get();120if target_entity == entity {121warn!(122"{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.",123caller.map(|location|format!("{location}: ")).unwrap_or_default(),124DebugName::type_name::<Self>(),125DebugName::type_name::<Self>()126);127world.commands().entity(entity).remove::<Self>();128return;129}130// For one-to-one relationships, remove existing relationship before adding new one131let current_source_to_remove = world132.get_entity(target_entity)133.ok()134.and_then(|target_entity_ref| target_entity_ref.get::<Self::RelationshipTarget>())135.and_then(|relationship_target| {136relationship_target137.collection()138.source_to_remove_before_add()139});140141if let Some(current_source) = current_source_to_remove {142world.commands().entity(current_source).try_remove::<Self>();143}144145if let Ok(mut entity_commands) = world.commands().get_entity(target_entity) {146// Deferring is necessary for batch mode147entity_commands148.entry::<Self::RelationshipTarget>()149.and_modify(move |mut relationship_target| {150relationship_target.collection_mut_risky().add(entity);151})152.or_insert_with(move || {153let mut target = Self::RelationshipTarget::with_capacity(1);154target.collection_mut_risky().add(entity);155target156});157} else {158warn!(159"{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.",160caller.map(|location|format!("{location}: ")).unwrap_or_default(),161DebugName::type_name::<Self>(),162DebugName::type_name::<Self>()163);164world.commands().entity(entity).remove::<Self>();165}166}167168/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.169// note: think of this as "on_drop"170fn on_replace(171mut world: DeferredWorld,172HookContext {173entity,174relationship_hook_mode,175..176}: HookContext,177) {178match relationship_hook_mode {179RelationshipHookMode::Run => {}180RelationshipHookMode::Skip => return,181RelationshipHookMode::RunIfNotLinked => {182if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {183return;184}185}186}187let target_entity = world.entity(entity).get::<Self>().unwrap().get();188if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) {189if let Some(mut relationship_target) =190target_entity_mut.get_mut::<Self::RelationshipTarget>()191{192relationship_target.collection_mut_risky().remove(entity);193if relationship_target.len() == 0 {194let command = |mut entity: EntityWorldMut| {195// this "remove" operation must check emptiness because in the event that an identical196// relationship is inserted on top, this despawn would result in the removal of that identical197// relationship ... not what we want!198if entity199.get::<Self::RelationshipTarget>()200.is_some_and(RelationshipTarget::is_empty)201{202entity.remove::<Self::RelationshipTarget>();203}204};205206world207.commands()208.queue_silenced(command.with_entity(target_entity));209}210}211}212}213}214215/// The iterator type for the source entities in a [`RelationshipTarget`] collection,216/// as defined in the [`RelationshipSourceCollection`] trait.217pub type SourceIter<'w, R> =218<<R as RelationshipTarget>::Collection as RelationshipSourceCollection>::SourceIter<'w>;219220/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type.221/// See the [`Relationship`] documentation for more information.222pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {223/// 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.224///225/// 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).226/// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning).227///228/// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone.229/// This defaults to false when derived.230const LINKED_SPAWN: bool;231/// The [`Relationship`] that populates this [`RelationshipTarget`] collection.232type Relationship: Relationship<RelationshipTarget = Self>;233/// The collection type that stores the "source" entities for this [`RelationshipTarget`] component.234///235/// Check the list of types which implement [`RelationshipSourceCollection`] for the data structures that can be used inside of your component.236/// If you need a new collection type, you can implement the [`RelationshipSourceCollection`] trait237/// for a type you own which wraps the collection you want to use (to avoid the orphan rule),238/// or open an issue on the Bevy repository to request first-party support for your collection type.239type Collection: RelationshipSourceCollection;240241/// Returns a reference to the stored [`RelationshipTarget::Collection`].242fn collection(&self) -> &Self::Collection;243/// Returns a mutable reference to the stored [`RelationshipTarget::Collection`].244///245/// # Warning246/// This should generally not be called by user code, as modifying the internal collection could invalidate the relationship.247/// The collection should not contain duplicates.248fn collection_mut_risky(&mut self) -> &mut Self::Collection;249250/// Creates a new [`RelationshipTarget`] from the given [`RelationshipTarget::Collection`].251///252/// # Warning253/// This should generally not be called by user code, as constructing the internal collection could invalidate the relationship.254/// The collection should not contain duplicates.255fn from_collection_risky(collection: Self::Collection) -> Self;256257/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.258// note: think of this as "on_drop"259fn on_replace(260mut world: DeferredWorld,261HookContext {262entity,263relationship_hook_mode,264..265}: HookContext,266) {267match relationship_hook_mode {268RelationshipHookMode::Run => {}269// For RelationshipTarget we don't want to run this hook even if it isn't linked, but for Relationship we do.270RelationshipHookMode::Skip | RelationshipHookMode::RunIfNotLinked => return,271}272let (entities, mut commands) = world.entities_and_commands();273let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();274for source_entity in relationship_target.iter() {275commands276.entity(source_entity)277.try_remove::<Self::Relationship>();278}279}280281/// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when282/// that entity is despawned.283// note: think of this as "on_drop"284fn on_despawn(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {285let (entities, mut commands) = world.entities_and_commands();286let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();287for source_entity in relationship_target.iter() {288commands.entity(source_entity).try_despawn();289}290}291292/// Creates this [`RelationshipTarget`] with the given pre-allocated entity capacity.293fn with_capacity(capacity: usize) -> Self {294let collection =295<Self::Collection as RelationshipSourceCollection>::with_capacity(capacity);296Self::from_collection_risky(collection)297}298299/// Iterates the entities stored in this collection.300#[inline]301fn iter(&self) -> SourceIter<'_, Self> {302self.collection().iter()303}304305/// Returns the number of entities in this collection.306#[inline]307fn len(&self) -> usize {308self.collection().len()309}310311/// Returns true if this entity collection is empty.312#[inline]313fn is_empty(&self) -> bool {314self.collection().is_empty()315}316}317318/// The "clone behavior" for [`RelationshipTarget`]. The [`RelationshipTarget`] will be populated with the proper components319/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities320/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that!321///322/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured323/// to spawn recursively.324pub fn clone_relationship_target<T: RelationshipTarget>(325component: &T,326cloned: &mut T,327context: &mut ComponentCloneCtx,328) {329if context.linked_cloning() && T::LINKED_SPAWN {330let collection = cloned.collection_mut_risky();331for entity in component.iter() {332collection.add(entity);333context.queue_entity_clone(entity);334}335} else if context.moving() {336let target = context.target();337let collection = cloned.collection_mut_risky();338for entity in component.iter() {339collection.add(entity);340context.queue_deferred(move |world, _mapper| {341// We don't want relationships hooks to run because we are manually constructing the collection here342_ = DeferredWorld::from(world)343.modify_component_with_relationship_hook_mode::<T::Relationship, ()>(344entity,345RelationshipHookMode::Skip,346|r| r.set_risky(target),347);348});349}350}351}352353/// Configures the conditions under which the Relationship insert/replace hooks will be run.354#[derive(Copy, Clone, Debug)]355pub enum RelationshipHookMode {356/// Relationship insert/replace hooks will always run357Run,358/// Relationship insert/replace hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false359RunIfNotLinked,360/// Relationship insert/replace hooks will always be skipped361Skip,362}363364/// Wrapper for components clone specialization using autoderef.365#[doc(hidden)]366pub struct RelationshipCloneBehaviorSpecialization<T>(PhantomData<T>);367368impl<T> Default for RelationshipCloneBehaviorSpecialization<T> {369fn default() -> Self {370Self(PhantomData)371}372}373374/// Base trait for relationship clone specialization using autoderef.375#[doc(hidden)]376pub trait RelationshipCloneBehaviorBase {377fn default_clone_behavior(&self) -> ComponentCloneBehavior;378}379380impl<C> RelationshipCloneBehaviorBase for RelationshipCloneBehaviorSpecialization<C> {381fn default_clone_behavior(&self) -> ComponentCloneBehavior {382// Relationships currently must have `Clone`/`Reflect`-based handler for cloning/moving logic to properly work.383ComponentCloneBehavior::Ignore384}385}386387/// Specialized trait for relationship clone specialization using autoderef.388#[doc(hidden)]389pub trait RelationshipCloneBehaviorViaReflect {390fn default_clone_behavior(&self) -> ComponentCloneBehavior;391}392393#[cfg(feature = "bevy_reflect")]394impl<C: Relationship + bevy_reflect::Reflect> RelationshipCloneBehaviorViaReflect395for &RelationshipCloneBehaviorSpecialization<C>396{397fn default_clone_behavior(&self) -> ComponentCloneBehavior {398ComponentCloneBehavior::reflect()399}400}401402/// Specialized trait for relationship clone specialization using autoderef.403#[doc(hidden)]404pub trait RelationshipCloneBehaviorViaClone {405fn default_clone_behavior(&self) -> ComponentCloneBehavior;406}407408impl<C: Relationship + Clone> RelationshipCloneBehaviorViaClone409for &&RelationshipCloneBehaviorSpecialization<C>410{411fn default_clone_behavior(&self) -> ComponentCloneBehavior {412ComponentCloneBehavior::clone::<C>()413}414}415416/// Specialized trait for relationship target clone specialization using autoderef.417#[doc(hidden)]418pub trait RelationshipTargetCloneBehaviorViaReflect {419fn default_clone_behavior(&self) -> ComponentCloneBehavior;420}421422#[cfg(feature = "bevy_reflect")]423impl<C: RelationshipTarget + bevy_reflect::Reflect + bevy_reflect::TypePath>424RelationshipTargetCloneBehaviorViaReflect for &&&RelationshipCloneBehaviorSpecialization<C>425{426fn default_clone_behavior(&self) -> ComponentCloneBehavior {427ComponentCloneBehavior::Custom(|source, context| {428if let Some(component) = source.read::<C>()429&& let Ok(mut cloned) = component.reflect_clone_and_take::<C>()430{431cloned.collection_mut_risky().clear();432clone_relationship_target(component, &mut cloned, context);433context.write_target_component(cloned);434}435})436}437}438439/// Specialized trait for relationship target clone specialization using autoderef.440#[doc(hidden)]441pub trait RelationshipTargetCloneBehaviorViaClone {442fn default_clone_behavior(&self) -> ComponentCloneBehavior;443}444445impl<C: RelationshipTarget + Clone> RelationshipTargetCloneBehaviorViaClone446for &&&&RelationshipCloneBehaviorSpecialization<C>447{448fn default_clone_behavior(&self) -> ComponentCloneBehavior {449ComponentCloneBehavior::Custom(|source, context| {450if let Some(component) = source.read::<C>() {451let mut cloned = component.clone();452cloned.collection_mut_risky().clear();453clone_relationship_target(component, &mut cloned, context);454context.write_target_component(cloned);455}456})457}458}459460/// We know there's no additional data on Children, so this handler is an optimization to avoid cloning the entire Collection.461#[doc(hidden)]462pub trait RelationshipTargetCloneBehaviorHierarchy {463fn default_clone_behavior(&self) -> ComponentCloneBehavior;464}465466impl RelationshipTargetCloneBehaviorHierarchy467for &&&&&RelationshipCloneBehaviorSpecialization<crate::hierarchy::Children>468{469fn default_clone_behavior(&self) -> ComponentCloneBehavior {470ComponentCloneBehavior::Custom(|source, context| {471if let Some(component) = source.read::<crate::hierarchy::Children>() {472let mut cloned = crate::hierarchy::Children::with_capacity(component.len());473clone_relationship_target(component, &mut cloned, context);474context.write_target_component(cloned);475}476})477}478}479480#[cfg(test)]481mod tests {482use core::marker::PhantomData;483484use crate::prelude::{ChildOf, Children};485use crate::world::World;486use crate::{component::Component, entity::Entity};487use alloc::vec::Vec;488489#[test]490fn custom_relationship() {491#[derive(Component)]492#[relationship(relationship_target = LikedBy)]493struct Likes(pub Entity);494495#[derive(Component)]496#[relationship_target(relationship = Likes)]497struct LikedBy(Vec<Entity>);498499let mut world = World::new();500let a = world.spawn_empty().id();501let b = world.spawn(Likes(a)).id();502let c = world.spawn(Likes(a)).id();503assert_eq!(world.entity(a).get::<LikedBy>().unwrap().0, &[b, c]);504}505506#[test]507fn self_relationship_fails() {508#[derive(Component)]509#[relationship(relationship_target = RelTarget)]510struct Rel(Entity);511512#[derive(Component)]513#[relationship_target(relationship = Rel)]514struct RelTarget(Vec<Entity>);515516let mut world = World::new();517let a = world.spawn_empty().id();518world.entity_mut(a).insert(Rel(a));519assert!(!world.entity(a).contains::<Rel>());520assert!(!world.entity(a).contains::<RelTarget>());521}522523#[test]524fn relationship_with_missing_target_fails() {525#[derive(Component)]526#[relationship(relationship_target = RelTarget)]527struct Rel(Entity);528529#[derive(Component)]530#[relationship_target(relationship = Rel)]531struct RelTarget(Vec<Entity>);532533let mut world = World::new();534let a = world.spawn_empty().id();535world.despawn(a);536let b = world.spawn(Rel(a)).id();537assert!(!world.entity(b).contains::<Rel>());538assert!(!world.entity(b).contains::<RelTarget>());539}540541#[test]542fn relationship_with_multiple_non_target_fields_compiles() {543#[derive(Component)]544#[relationship(relationship_target=Target)]545#[expect(dead_code, reason = "test struct")]546struct Source {547#[relationship]548target: Entity,549foo: u8,550bar: u8,551}552553#[derive(Component)]554#[relationship_target(relationship=Source)]555struct Target(Vec<Entity>);556557// No assert necessary, looking to make sure compilation works with the macros558}559#[test]560fn relationship_target_with_multiple_non_target_fields_compiles() {561#[derive(Component)]562#[relationship(relationship_target=Target)]563struct Source(Entity);564565#[derive(Component)]566#[relationship_target(relationship=Source)]567#[expect(dead_code, reason = "test struct")]568struct Target {569#[relationship]570target: Vec<Entity>,571foo: u8,572bar: u8,573}574575// No assert necessary, looking to make sure compilation works with the macros576}577578#[test]579fn relationship_with_multiple_unnamed_non_target_fields_compiles() {580#[derive(Component)]581#[relationship(relationship_target=Target<T>)]582struct Source<T: Send + Sync + 'static>(#[relationship] Entity, PhantomData<T>);583584#[derive(Component)]585#[relationship_target(relationship=Source<T>)]586struct Target<T: Send + Sync + 'static>(#[relationship] Vec<Entity>, PhantomData<T>);587588// No assert necessary, looking to make sure compilation works with the macros589}590591#[test]592fn parent_child_relationship_with_custom_relationship() {593#[derive(Component)]594#[relationship(relationship_target = RelTarget)]595struct Rel(Entity);596597#[derive(Component)]598#[relationship_target(relationship = Rel)]599struct RelTarget(Entity);600601let mut world = World::new();602603// Rel on Parent604// Despawn Parent605let mut commands = world.commands();606let child = commands.spawn_empty().id();607let parent = commands.spawn(Rel(child)).add_child(child).id();608commands.entity(parent).despawn();609world.flush();610611assert!(world.get_entity(child).is_err());612assert!(world.get_entity(parent).is_err());613614// Rel on Parent615// Despawn Child616let mut commands = world.commands();617let child = commands.spawn_empty().id();618let parent = commands.spawn(Rel(child)).add_child(child).id();619commands.entity(child).despawn();620world.flush();621622assert!(world.get_entity(child).is_err());623assert!(!world.entity(parent).contains::<Rel>());624625// Rel on Child626// Despawn Parent627let mut commands = world.commands();628let parent = commands.spawn_empty().id();629let child = commands.spawn((ChildOf(parent), Rel(parent))).id();630commands.entity(parent).despawn();631world.flush();632633assert!(world.get_entity(child).is_err());634assert!(world.get_entity(parent).is_err());635636// Rel on Child637// Despawn Child638let mut commands = world.commands();639let parent = commands.spawn_empty().id();640let child = commands.spawn((ChildOf(parent), Rel(parent))).id();641commands.entity(child).despawn();642world.flush();643644assert!(world.get_entity(child).is_err());645assert!(!world.entity(parent).contains::<RelTarget>());646}647648#[test]649fn spawn_batch_with_relationship() {650let mut world = World::new();651let parent = world.spawn_empty().id();652let children = world653.spawn_batch((0..10).map(|_| ChildOf(parent)))654.collect::<Vec<_>>();655656for &child in &children {657assert!(world658.get::<ChildOf>(child)659.is_some_and(|child_of| child_of.parent() == parent));660}661assert!(world662.get::<Children>(parent)663.is_some_and(|children| children.len() == 10));664}665666#[test]667fn insert_batch_with_relationship() {668let mut world = World::new();669let parent = world.spawn_empty().id();670let child = world.spawn_empty().id();671world.insert_batch([(child, ChildOf(parent))]);672world.flush();673674assert!(world.get::<ChildOf>(child).is_some());675assert!(world.get::<Children>(parent).is_some());676}677}678679680