Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add World::entity_scope to split the borrow on the World by extracting a single entity #13128

Open
alice-i-cecile opened this issue Apr 28, 2024 · 5 comments
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help!

Comments

@alice-i-cecile
Copy link
Member

What problem does this solve or what need does it fill?

When working with avatar-centric games (like platformers or FPS games), one object (the player avatar) is typically wildly more complex and important than others. Writing exclusive systems that fetch "the player" and operate on it in a game-object style can be an effective programming pattern.

In order to make this work, we often want to access that highly important entity, and relate it to "everything else around it". The current most obvious pattern is to fetch that data, clone or remove it out of the world, and then operate on it.

What solution would you like?

Add World::entity_scope, which works like resource_scope, but returns an EntityMut instead of a resource, in addition to a reference to the rest of the world.

This could be done by removing and then re-adding the entity but:

  1. We need to avoid blanket-triggering change detection on all of the components: adding / inserting the object is
  2. The Entity id of the object should remain unchanged.

A strong form of entity disabling (#11090), which makes it fully impossible to access the data of the object would be a clean way to do this, but would have more significant architectural implications. This would also come with less caveats: being able to add children to the requested entity without immediately panicking would be great.

What alternative(s) have you considered?

An EntityWorldMut would be strictly more powerful, and makes more conceptual sense: being able to freely add and remove components would be really useful. However, actually providing that access is fundamentally unsound with the archetypal ECS that Bevy uses: changing this data requires changes to the metadata of the component storage itself, and requires a true hard lock.

Users could manually implement this pattern, by despawning and then spawning the player object, but without a way to clone entities (#1515) or otherwise extract all of their component, actually doing so is tedious.

Furthermore, any existing references to that entity (such as Parent components!) will break, since the Entity ID will be changed.
Change detection will also be triggered on every component, effectively rendering it useless for their game object style entity.

Additional context

This is a conceptual sister to #13127: both are in the service of making the "get and work with a single entity in a loosely typed way" workflow easier.

Performance is always nice, but is a secondary concern for this style of API.

An equivalent hierarchy_scope, which extracts a whole tree of entities, is probably useful down the line as well: player objects are often conceptually composed of complete trees in order to support complex animated models.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help! labels Apr 28, 2024
@SkiFire13
Copy link
Contributor

Returning an actual &mut World would be unsound even with any form of entity disabling, as it still allows you to do structural changes that might affect the disabled entity. And there's also the extreme case where the World is replaced (i.e. by doing *world = World::new();)

@atornity
Copy link
Contributor

atornity commented Jun 8, 2024

Returning an actual &mut World would be unsound even with any form of entity disabling

how about instead of having access to the whole entity (EntityMut) - you just get access to a set of components?

let _ = world.entity_mut(entity).component_scope::<(Position, Health), _>(|world, (p, h)| {
   // blabla
}).unwrap(); // panics if either Position or Health doesn't exist

this can almost be done already with EntityWorldMut::take + EntityWorldMut::insert but it triggers Added (bad) and it's surprisingly easy to gorget to insert the component back afterwards.

@SkiFire13
Copy link
Contributor

An approach similar to EntityWorldMut::take + EntityWorldMut::insert could work, but I don't see a way to optimize it to avoid the archetype move, which is pretty costly.

@ItsDoot
Copy link
Contributor

ItsDoot commented Oct 10, 2024

Returning an actual &mut World would be unsound even with any form of entity disabling, as it still allows you to do structural changes that might affect the disabled entity. And there's also the extreme case where the World is replaced (i.e. by doing *world = World::new();)

What if we return a &mut DeferredWorld, since it doesn't allow structural changes?

@SkiFire13
Copy link
Contributor

What if we return a &mut DeferredWorld, since it doesn't allow structural changes?

I think that might work, but it will likely still be pretty tricky to do correctly, especially if you want to recursively call entity_scope on another entity (since it seems the main ideas for how to implement entity_scope involve doing structural changes)

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-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help!
Projects
None yet
Development

No branches or pull requests

4 participants