EntityRef/Mut get_components
Objective
Implements #13127
EntityRef / EntityMut usage is currently unnecessarily verbose and borrow-checker-constrained. It is currently impossible to use EntityMut to get mutable references to more than one component at the same time:
let mut entity = world.entity_mut(some_entity);
let mut a = entity.get_mut::<A>();
// this fails to compile because `entity` is already borrowed mutably
let mut b = entity.get_mut::<B>();
This makes EntityMut significantly less useful than it could be. Querying necessary components on demand is a pattern many prefer to use over traditional top-level queries.
Solution
Implement new get_components and components methods for EntityRef and EntityMut (and mutable variants for EntityMut) to provide multi-component access:
let (a, b) = world.entity(SOME_ENTITY).components::<(&A, &B)>();
let (a, b) = world.entity(SOME_ENTITY).get_components::<(&A, &B)>().unwrap();
let (mut a, b) = world.entity_mut(SOME_ENTITY).components_mut::<(&mut A, &B)>();
let (mut a, b) = world.entity_mut(SOME_ENTITY).get_components_mut::<(&mut A, &B)>().unwrap();
This is implemented using our existing Query implementation to provide multi-component access. It checks access without actually computing QueryState / access using the very convenient Q::matches_component_set.
Next Steps
- This doesn't add
FilteredEntityRefandFilteredEntityMutvariants, as checking that access is actually more challenging (and with the current methods, likely prohibitively expensive because I think we need to computeAccess<ComponentId>) - This doesn't do the
componentstogetandget_componentstotry_getrenaming / unification I proposed in #13127 as I suspect that will be more controversial, and it also touches a lot of code.
Changelog
Added
-
EntityRef::components,EntityRef::get_components,EntityMut::components,EntityMut::get_components,EntityMut::components_mut, andEntityMut::get_components_mut.
Changed
- Renamed
FilteredEntityRef::componentstoFilteredEntityRef::accessed_componentsandFilteredEntityMut::componentstoFilteredEntityMut::accessed_componentsto avoid future name clashes
Migration Guide
- Rename
FilteredEntityRef::componentstoFilteredEntityRef::accessed_componentsandFilteredEntityMut::componentstoFilteredEntityMut::accessed_components
In light of #12660, I would vote against adding more panicking shorthands like components.
In light of https://github.com/bevyengine/bevy/issues/12660, I would vote against adding more panicking shorthands like components.
I think discouraging unwrap/panicking variants entirely (as in, not providing impls) would have pretty nasty ergonomics implications for people, especially with the current Option vs Result compatibility problem for ?. I would be very uncomfortable removing those apis right now.
In light of #12660, I would vote against adding more panicking shorthands like components.
I think discouraging unwrap/panicking variants entirely (as in, not providing impls) would have pretty nasty ergonomics implications for people, especially with the current Option vs Result compatibility problem for
?. I would be very uncomfortable removing those apis right now.
I agree somewhat with this. It's frustrating when something like despawn causes a panic due to an unwrap, but if I want to shoot myself in the foot with something like single or components_mut I should be allowed to.
Left some replies in #12660
Since this uses QueryData, this would allow doing stuff like this, right?
world.entity(e).get_components::<(Entity, &X, Option<&Y>, Has<Z>)>();
"components" sorta makes sense for Option/Has... Is Entity a component though? What if QueryData gets implemented with more Component-adjacent stuff in the future?
I'm guessing the next steps item 2 would be renaming these to the get functions in the future?
I find this kind of funny. Thinking about this historically, this feels like we've inverted the relationship between Query::get_component and EntityMut/EntityRef with this change.
Haha yeah thats an interesting comparison. Although I think this is significantly more defensible / necessary than Query::get_component.
Since this uses QueryData, this would allow doing stuff like this, right?
Yup!
I'm guessing the next steps item 2 would be renaming these to the get functions in the future?
Yup that is something discussed in the Next Steps section above. Haven't thought of a better interim name. And components is approximately correct.
Welp this PR is sadly stalled until we sort out this problem: https://github.com/bevyengine/bevy/pull/13375#discussion_r1602135098
Not sure there will be a solution that doesn't involve doing access checks on each call.
I think the best we can hope for is "allocation free check every entry against every other entry" (similar to query.many_mut), but accomplishing that will require significant changes (or at least, additions) to how we compute access.
I think we should be able to reuse the access mechanisms from queries to validate soundness here. Would need to sit down with the code to figure out exactly how. I much prefer that over creating another mechanism though, especially since I'd like to be able to add untyped variants to this in the future.
Would it be possible to merge a version of this PR with just the immutable get_components variants for 0.15? Even just the immutable variants would be very useful for me.
I agree the an immutable form would be useful still. Feel free to spin off a PR that does that please.
Closing in favor of #15089; further attempts to implement the mutable form will be easier in a separate PR.