Skip to content

Conversation

eugineerd
Copy link
Contributor

Objective

Currently there is no way to traverse relationships in type-erased contexts or to define dynamic relationship components, which is a hole in the current relationships api.

Solution

Introduce RelationshipAccessor to describe a way to get Entity values from any registered relationships in dynamic contexts and store it on ComponentDescriptor. This allows to traverse relationships without knowing their type, which is useful for working with entity hierarchies using non-default components.

Testing

Added a simple test/example of how to use this api to traverse hierarchies in a type-erased context.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events X-Uncontroversial This work is generally agreed upon D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 19, 2025
Copy link
Contributor

@urben1680 urben1680 left a comment

Choose a reason for hiding this comment

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

I really like this feature and already see me using this. 👍

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 20, 2025
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Oct 20, 2025
Merged via the queue into bevyengine:main with commit a264dd3 Oct 20, 2025
38 checks passed
@chescock
Copy link
Contributor

This looks good! I see I'm too late to give an official approval, but I still wanted to leave a few thoughts:

Will we want the RelationshipAccessor to include the ComponentId of the reverse direction? Like, ChildOf would tell you Children and vice versa?

Given how few components are relationships, would we want to do something like relationship_accessor: Option<Box<RelationshipAccessor>>, to make the ComponentDescriptor smaller in the common case? I guess right now it's only two usizes, so that wouldn't actually save much.

@eugineerd
Copy link
Contributor Author

Will we want the RelationshipAccessor to include the ComponentId of the reverse direction? Like, ChildOf would tell you Children and vice versa?

I've thought about adding that, but that'll require initializing this field after creating the ComponentDescriptor for a Component (or passing Components to ComponentDescriptor::new, which is actually not too bad as it's available in those contexts either way). I'm not entirely sure what use case that would be useful for though. The only one I can come up with is removing the relationship when traversing them from the RelationshipTarget side, which might be useful. But I do agree that this is something that should be stored to make relationships support more first-class in the untyped context.

Given how few components are relationships, would we want to do something like relationship_accessor: Option<Box<RelationshipAccessor>>, to make the ComponentDescriptor smaller in the common case? I guess right now it's only two usizes, so that wouldn't actually save much.

Yeah, that makes sense. Ideally we'd have components-as-entities so that ComponentDescriptor can be split up, but even then making a safe interface around that would be quite a challenge.

@urben1680 also mentioned that a flag indicating whether there is any other data besides Entity/Collection present on these components will be useful, so maybe these can be put in a follow up PR (along with changes required for whatever solution we land on for many-to-many relationships).

@chescock
Copy link
Contributor

I've thought about adding that, but that'll require initializing this field after creating the ComponentDescriptor for a Component

Oh, right, there are links in both directions, so you can't just register one component first and then pass it to the other. Yeah, that's harder than I thought!

I'm not entirely sure what use case that would be useful for though.

The reason it's on my mind is that I'm trying to figure out how to safely allow mutable query access through Children for #17647, and my current thought is that we'll need to read the ChildOf component and ensure it matches the expected parent. If that does turn out to be the way to do that, then we'll eventually want an untyped way to do that.

@eugineerd
Copy link
Contributor Author

The reason it's on my mind is that I'm trying to figure out how to safely allow mutable query access through Children

Because we can't trust that RelationshipTarget's data is correct since there are escape hatches for mutating it? I guess that wouldn't be a problem if RelationshipTarget's collection mutation was unsafe and also guaranteed to contain unique entities? Makes sense, although that won't work for more than 1 level of relationship traversal (i.e. Query<(&mut Comp, Related<Children, (&mut Comp, Related<OtherRelation, &mut Comp>)>)>), right? But I do see how it can be useful for that, yes.

mate-h pushed a commit to mate-h/bevy that referenced this pull request Oct 22, 2025
…ic contexts (bevyengine#21601)

# Objective
Currently there is no way to traverse relationships in type-erased
contexts or to define dynamic relationship components, which is a hole
in the current relationships api.

## Solution
Introduce `RelationshipAccessor` to describe a way to get `Entity`
values from any registered relationships in dynamic contexts and store
it on `ComponentDescriptor`. This allows to traverse relationships
without knowing their type, which is useful for working with entity
hierarchies using non-default components.

## Testing
Added a simple test/example of how to use this api to traverse
hierarchies in a type-erased context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Uncontroversial This work is generally agreed upon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants