//! Entities generally don't exist in isolation. Instead, they are related to other entities in various ways.1//! While Bevy comes with a built-in [`ChildOf`]/[`Children`] relationship2//! (which enables transform and visibility propagation),3//! you can define your own relationships using components.4//!5//! We can define a custom relationship by creating two components:6//! one to store the relationship itself, and another to keep track of the reverse relationship.7//! Bevy's [`ChildOf`] component implements the [`Relationship`] trait, serving as the source of truth,8//! while the [`Children`] component implements the [`RelationshipTarget`] trait and is used to accelerate traversals down the hierarchy.9//!10//! In this example we're creating a [`Targeting`]/[`TargetedBy`] relationship,11//! demonstrating how you might model units which target a single unit in combat.1213use bevy::ecs::entity::EntityHashSet;14use bevy::ecs::system::RunSystemOnce;15use bevy::prelude::*;1617/// The entity that this entity is targeting.18///19/// This is the source of truth for the relationship,20/// and can be modified directly to change the target.21#[derive(Component, Debug)]22#[relationship(relationship_target = TargetedBy)]23struct Targeting(Entity);2425/// All entities that are targeting this entity.26///27/// This component is updated reactively using the component hooks introduced by deriving28/// the [`Relationship`] trait. We should not modify this component directly,29/// but can safely read its field. In a larger project, we could enforce this through the use of30/// private fields and public getters.31#[derive(Component, Debug)]32#[relationship_target(relationship = Targeting)]33struct TargetedBy(Vec<Entity>);3435fn main() {36// Operating on a raw `World` and running systems one at a time37// is great for writing tests and teaching abstract concepts!38let mut world = World::new();3940// We're going to spawn a few entities and relate them to each other in a complex way.41// To start, Bob will target Alice, Charlie will target Bob,42// and Alice will target Charlie. This creates a loop in the relationship graph.43//44// Then, we'll spawn Devon, who will target Charlie,45// creating a more complex graph with a branching structure.46fn spawning_entities_with_relationships(mut commands: Commands) {47// Calling .id() after spawning an entity will return the `Entity` identifier of the spawned entity,48// even though the entity itself is not yet instantiated in the world.49// This works because Commands will reserve the entity ID before actually spawning the entity,50// through the use of atomic counters.51let alice = commands.spawn(Name::new("Alice")).id();52// Relations are just components, so we can add them into the bundle that we're spawning.53let bob = commands.spawn((Name::new("Bob"), Targeting(alice))).id();5455// The `with_related` and `with_related_entities` helper methods on `EntityCommands` can be used to add relations in a more ergonomic way.56let charlie = commands57.spawn((Name::new("Charlie"), Targeting(bob)))58// The `with_related` method will spawn a bundle with `Targeting` relationship59.with_related::<Targeting>(Name::new("James"))60// The `with_related_entities` method will automatically add the `Targeting` component to any entities spawned within the closure,61// targeting the entity that we're calling `with_related` on.62.with_related_entities::<Targeting>(|related_spawner_commands| {63// We could spawn multiple entities here, and they would all target `charlie`.64related_spawner_commands.spawn(Name::new("Devon"));65})66.id();6768// Simply inserting the `Targeting` component will automatically create and update the `TargetedBy` component on the target entity.69// We can do this at any point; not just when the entity is spawned.70commands.entity(alice).insert(Targeting(charlie));71}7273world74.run_system_once(spawning_entities_with_relationships)75.unwrap();7677fn debug_relationships(78// Not all of our entities are targeted by something, so we use `Option` in our query to handle this case.79relations_query: Query<(&Name, &Targeting, Option<&TargetedBy>)>,80name_query: Query<&Name>,81) {82let mut relationships = String::new();8384for (name, targeting, maybe_targeted_by) in relations_query.iter() {85let targeting_name = name_query.get(targeting.0).unwrap();86let targeted_by_string = if let Some(targeted_by) = maybe_targeted_by {87let mut vec_of_names = Vec::<&Name>::new();8889for entity in targeted_by.iter() {90let name = name_query.get(entity).unwrap();91vec_of_names.push(name);92}9394// Convert this to a nice string for printing.95let vec_of_str: Vec<&str> = vec_of_names.iter().map(|name| name.as_str()).collect();96vec_of_str.join(", ")97} else {98"nobody".to_string()99};100101relationships.push_str(&format!(102"{name} is targeting {targeting_name}, and is targeted by {targeted_by_string}\n",103));104}105106println!("{relationships}");107}108109world.run_system_once(debug_relationships).unwrap();110111// Demonstrates how to correctly mutate relationships.112// Relationship components are immutable! We can't query for the `Targeting` component mutably and modify it directly,113// but we can insert a new `Targeting` component to replace the old one.114// This allows the hooks on the `Targeting` component to update the `TargetedBy` component correctly.115// The `TargetedBy` component will be updated automatically!116fn mutate_relationships(name_query: Query<(Entity, &Name)>, mut commands: Commands) {117// Let's find Devon by doing a linear scan of the entity names.118let devon = name_query119.iter()120.find(|(_entity, name)| name.as_str() == "Devon")121.unwrap()122.0;123124let alice = name_query125.iter()126.find(|(_entity, name)| name.as_str() == "Alice")127.unwrap()128.0;129130println!("Making Devon target Alice.\n");131commands.entity(devon).insert(Targeting(alice));132}133134world.run_system_once(mutate_relationships).unwrap();135world.run_system_once(debug_relationships).unwrap();136137// Systems can return errors,138// which can be used to signal that something went wrong during the system's execution.139#[derive(Debug)]140#[expect(141dead_code,142reason = "Rust considers types that are only used by their debug trait as dead code."143)]144struct TargetingCycle {145initial_entity: Entity,146visited: EntityHashSet,147}148149/// Bevy's relationships come with all sorts of useful methods for traversal.150/// Here, we're going to look for cycles using a depth-first search.151fn check_for_cycles(152// We want to check every entity for cycles153query_to_check: Query<Entity, With<Targeting>>,154// Fetch the names for easier debugging.155name_query: Query<&Name>,156// The targeting_query allows us to traverse the relationship graph.157targeting_query: Query<&Targeting>,158) -> Result<(), TargetingCycle> {159for initial_entity in query_to_check.iter() {160let mut visited = EntityHashSet::new();161let mut targeting_name = name_query.get(initial_entity).unwrap().clone();162println!("Checking for cycles starting at {targeting_name}",);163164// There's all sorts of methods like this; check the `Query` docs for more!165// This would also be easy to do by just manually checking the `Targeting` component,166// and calling `query.get(targeted_entity)` on the entity that it targets in a loop.167for targeting in targeting_query.iter_ancestors(initial_entity) {168let target_name = name_query.get(targeting).unwrap();169println!("{targeting_name} is targeting {target_name}",);170targeting_name = target_name.clone();171172if !visited.insert(targeting) {173return Err(TargetingCycle {174initial_entity,175visited,176});177}178}179}180181// If we've checked all the entities and haven't found a cycle, we're good!182Ok(())183}184185// Calling `world.run_system_once` on systems which return Results gives us two layers of errors:186// the first checks if running the system failed, and the second checks if the system itself returned an error.187// We're unwrapping the first, but checking the output of the system itself.188let cycle_result = world.run_system_once(check_for_cycles).unwrap();189println!("{cycle_result:?} \n");190// We deliberately introduced a cycle during spawning!191assert!(cycle_result.is_err());192193// Now, let's demonstrate removing relationships and break the cycle.194fn untarget(mut commands: Commands, name_query: Query<(Entity, &Name)>) {195// Let's find Charlie by doing a linear scan of the entity names.196let charlie = name_query197.iter()198.find(|(_entity, name)| name.as_str() == "Charlie")199.unwrap()200.0;201202// We can remove the `Targeting` component to remove the relationship203// and break the cycle we saw earlier.204println!("Removing Charlie's targeting relationship.\n");205commands.entity(charlie).remove::<Targeting>();206}207208world.run_system_once(untarget).unwrap();209world.run_system_once(debug_relationships).unwrap();210// Cycle free!211let cycle_result = world.run_system_once(check_for_cycles).unwrap();212println!("{cycle_result:?} \n");213assert!(cycle_result.is_ok());214}215216217