diff --git a/examples/guide/relations.rs b/examples/guide/relations.rs index 3be00b6..3a2b822 100644 --- a/examples/guide/relations.rs +++ b/examples/guide/relations.rs @@ -5,14 +5,19 @@ use tracing_tree::HierarchicalLayer; fn main() -> anyhow::Result<()> { registry().with(HierarchicalLayer::default()).init(); + basic()?; + exclusive()?; + Ok(()) +} + +fn basic() -> anyhow::Result<()> { + let mut world = World::new(); // ANCHOR: relation_basic component! { child_of(id): (), } - let mut world = World::new(); - let parent = Entity::builder() .set(name(), "Parent".into()) .spawn(&mut world); @@ -93,3 +98,36 @@ fn main() -> anyhow::Result<()> { Ok(()) } + +fn exclusive() -> anyhow::Result<()> { + let mut world = World::new(); + + // ANCHOR: exclusive + component! { + child_of(parent): () => [ Exclusive ], + } + + let id1 = Entity::builder().spawn(&mut world); + let id2 = Entity::builder().spawn(&mut world); + + let id3 = Entity::builder() + .set_default(child_of(id1)) + .spawn(&mut world); + + let entity = world.entity_mut(id3).unwrap(); + + tracing::info!( + "relations of {id3}: {:?}", + entity.relations(child_of).map(|v| v.0).collect_vec() + ); + + world.set(id3, child_of(id2), ()).unwrap(); + + let entity = world.entity_mut(id3).unwrap(); + tracing::info!( + "relations of {id3}: {:?}", + entity.relations(child_of).map(|v| v.0).collect_vec() + ); + // ANCHOR_END: exclusive + Ok(()) +} diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md index cbb4563..f6d9991 100644 --- a/guide/src/fundamentals/relations.md +++ b/guide/src/fundamentals/relations.md @@ -51,6 +51,20 @@ The following shows a more complete example of how to traverse and calculate the {{ #include ../../../examples/guide/springs.rs:main }} ``` +# Exclusive relations + +Relations can be declared as exclusive, which means that only one relation of that type can exist on an entity at a time. This is useful for cases where you want to have a single parent or outgoing connection. + +**Note**: This does not prevent multiple entities from referencing the same entity, but rather an entity referencing multiple entities. + +When a new relation is added to an entity, any existing relation of the same type will be removed. + +This is the case for the included [`child_of`](https://docs.rs/flax/latest/flax/components/fn.child_of.html) relation. + +```rust +{{ #include ../../../examples/guide/relations.rs:exclusive }} +``` + ## Lifetime Relations are managed by the ECS and will automatically be cleaned up. When an entity is despawned all relations which reference it will be removed from the ECS. As such, a relation will never point to an invalid entity. diff --git a/tests/relations.rs b/tests/relations.rs index 01d02ac..f89f2b6 100644 --- a/tests/relations.rs +++ b/tests/relations.rs @@ -256,3 +256,28 @@ fn many_detach() { world.despawn_recursive(parent, child_of).unwrap(); } + +#[test] +fn exclusive() { + component! { + child_of(parent): () => [ Exclusive ], + } + + let mut world = World::new(); + + let id1 = Entity::builder().spawn(&mut world); + let id2 = Entity::builder().spawn(&mut world); + + let id3 = Entity::builder() + .set_default(child_of(id1)) + .spawn(&mut world); + + let entity = world.entity_mut(id3).unwrap(); + + assert_eq!(entity.relations(child_of).map(|v| v.0).collect_vec(), [id1]); + + world.set(id3, child_of(id2), ()).unwrap(); + + let entity = world.entity_mut(id3).unwrap(); + assert_eq!(entity.relations(child_of).map(|v| v.0).collect_vec(), [id2]) +}