Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions crates/bevy_ecs/src/relationship/relationship_source_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,35 @@ impl<const N: usize> RelationshipSourceCollection for SmallVec<[Entity; N]> {
}
}

impl RelationshipSourceCollection for Entity {
type SourceIter<'a> = core::iter::Once<Entity>;

fn with_capacity(_capacity: usize) -> Self {
Entity::PLACEHOLDER
}

fn add(&mut self, entity: Entity) {
*self = entity;
}

fn remove(&mut self, entity: Entity) {
if *self == entity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely feels sus. What if we impl'ed this for Option<Entity> instead?

Copy link
Contributor Author

@dmyyy dmyyy Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it that way at first. It felt weird to me that it's expressable for the RelationshipTarget to be None while the Relationship still points to that entity. To be fair - manually changing the RelationshipTarget to None is just as weird as setting the RelationshipTarget to some random entity (we mess up the relationship).

This felt like the more minimal change that preserves symmetry (but I'm open to changing my mind)

*self = Entity::PLACEHOLDER;
}
}

fn iter(&self) -> Self::SourceIter<'_> {
core::iter::once(*self)
}

fn len(&self) -> usize {
if *self == Entity::PLACEHOLDER {
return 0;
}
1
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -184,4 +213,58 @@ mod tests {
let collection = rel_target.collection();
assert_eq!(collection, &SmallVec::from_buf([a]));
}

#[test]
fn entity_relationship_source_collection() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);

#[derive(Component)]
#[relationship_target(relationship = Rel)]
struct RelTarget(Entity);

let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();

world.entity_mut(a).insert(Rel(b));

let rel_target = world.get::<RelTarget>(b).unwrap();
let collection = rel_target.collection();
assert_eq!(collection, &a);
}

#[test]
fn one_to_one_relationships() {
#[derive(Component)]
#[relationship(relationship_target = Below)]
struct Above(Entity);

#[derive(Component)]
#[relationship_target(relationship = Above)]
struct Below(Entity);

let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();

world.entity_mut(a).insert(Above(b));
assert_eq!(a, world.get::<Below>(b).unwrap().0);

// Verify removing target removes relationship
world.entity_mut(b).remove::<Below>();
assert!(world.get::<Above>(a).is_none());

// Verify removing relationship removes target
world.entity_mut(a).insert(Above(b));
world.entity_mut(a).remove::<Above>();
assert!(world.get::<Below>(b).is_none());

// Actually - a is above c now! Verify relationship was updated correctly
let c = world.spawn_empty().id();
world.entity_mut(a).insert(Above(c));
assert!(world.get::<Below>(b).is_none());
assert_eq!(a, world.get::<Below>(c).unwrap().0);
}
}