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

Disable and enable entities #11090

Open
alice-i-cecile opened this issue Dec 26, 2023 · 13 comments
Open

Disable and enable entities #11090

alice-i-cecile opened this issue Dec 26, 2023 · 13 comments
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers
Milestone

Comments

@alice-i-cecile
Copy link
Member

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

For gameplay or performance reasons, it is common to want to temporarily disable entities.

While disabled, they should generally not be interacted with in any way.

What solution would you like?

Add a special Disabled marker component. Entities with this component do not appear in any queries, unless the query contains With<Disabled> (or otherwise explicitly requests that component).

I'm indifferent to either hard-coding a single type for this, or making this behavior an associated constant on the Component type. The former is simpler, but the latter is end-user extensible and arguably more elegant.

What alternative(s) have you considered?

Respawning

We could save, despawn and then respawn the entities. However it is both simpler and faster to keep the entities alive but filter them from (most) queries.

Additionally, reading the data of despawned and stored entities is much more challenging.

Custom marker component

Users could add a Disabled marker component of their own, and then add Without<Disabled> to all of their systems.

However:

  1. This is a lot of boilerplate.
  2. There is no standard across the ecosystem.
  3. External systems will continue to operate on them.

Additional context

flecs has a Disabled component and a Prefab component that work in this fashion. It also has a Prefab component which works in the same way.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events D-Complex Quite challenging from either a design or technical perspective. Ask for help! labels Dec 26, 2023
@alice-i-cecile
Copy link
Member Author

[9:27 PM]Joy: flecs also supports disabling individual components in a nice opt-in way
[9:27 PM]Joy: component IDs have a flag bit to differentiate between a T and a toggleable T
[9:28 PM]Joy: calling e.disable_component(), will move the entity to a new archetype/table whose column for T has a bitset saying which entries are disabled and marks e's as disabled
[9:28 PM]Joy: (no move if it's already there)
[9:28 PM]Joy: enabling it again will just flip the bit
[9:29 PM]Joy: so iteration is only affected when iterating entries in those archetypes/tables

@Bluefinger
Copy link
Contributor

#9797 unblocks the implementation of this for making use of bit-level IDs, a la flecs. It would need to be a follow-up PR that then modifies the ID format to include the extra disable bit in the flag portion (the high component will then be 2 bit flag, 30 bit value versus the current 1 bit flag, 31 bit value).

@SIGSTACKFAULT
Copy link
Contributor

If we do the built-in disabled component we would need to give the choice of storage type

@urben1680
Copy link
Contributor

I would like that. I work on a crate where systems can be reversed. So far despawning an entity in reversible systems just add a marker component so it can be easily undone when reversing the command. I would have to ask the user to always filter on that marker. A built-in solution would be so much better.

@iiYese
Copy link
Contributor

iiYese commented Dec 26, 2023

I would prefer this be done via merging #9797 given that it's on the horizon and more flexible than a special Disabled component.

@alice-i-cecile
Copy link
Member Author

Can you explain how #9797 would be useful for this? I'm not immediately seeing the connection.

@iiYese
Copy link
Contributor

iiYese commented Dec 26, 2023

@alice-i-cecile Doing this via flag bits lets you disable components individually which avoids competing meanings for why something is "disabled" (these build up rather quickly). It also lets you implement an engine standard Disabled marker component on top of it rather than it being special case internal magic should that still be desired.

Edit: Engine defined disabled markers is better than user defined disabled markers. See this comment.

@alice-i-cecile
Copy link
Member Author

Gathering some of the important discussion from @maniwani on Discord into here:

[8:26 PM]Joy: the thing is, we want both fine-grained change detection and disabling to be something opted into at runtime
(we never added disabling because we didn't want to degrade iteration for all queries)
[8:26 PM]Joy: that's the complicated part
[8:28 PM]Joy: IMO the best way to achieve that is to exploit archetype fragmentation
[8:28 PM]Sander: I think that issue is for entity disabling which you can just do with a marker tag
[8:28 PM]Sander: Component disabling can be done with a bitset
[8:33 PM]Joy: if we reserve flag bits in ComponentId, we can use them to differentiate between "a plain T" and "a T you can toggle" (because their IDs are different, they'll be treated as distinct components)
[8:34 PM]Joy: then archetypes/tables that have "a plain T" and the ones that have "a T you can toggle" will be different, table columns for T in the first group won't have the bitset, but columns in the second group will
[8:35 PM]Joy: and then if we have an API like entity.disable_component::() and entity.enable_component::()
[8:38 PM]Joy: those would be exclusive operations (Commands::disable_component, EntityWorldMut::disable_component)
[8:38 PM]Joy:
if the entity has "a T you can toggle", just set the bit
if the entity has "a plain T", move it to the archetype that has "a T you can toggle" instead, then set the bit
[8:48 PM]Joy: so as far as "how hard is this to implement?" I think the main part is updating the Query, Table, and TableColumn logic to handle the conditionally-present bitset
[8:51 PM]Joy: the "reserve flag bits" might already be done (I think that was in the Identifier PR)
[9:00 PM]NiseVoid: This sounds kind of sketchy, that would mean there's 3 versions of T: A plain T, an enabled T and a disabled T ... But whats the difference between an enabled and plain T? 🤔
[9:01 PM]James 🦃: There's only 2 versions of T: normal T and toggle-able T
[9:01 PM]James 🦃: When matching normal T a query won't have to perform a check for whether it's enabled, where as for toggle-able it checks the bitset
[9:07 PM]Joy: exactly
[9:08 PM]Joy: queries iterate archetypes/tables
[9:08 PM]Joy: so if you query for T
[9:09 PM]Joy: when the iterator is on a table with a normal T (which we can tell from the ID), we can skip the check
[9:10 PM]Joy: when the iterator is on a table with a toggle-able T (again, we can tell from the ID), we do the check
[9:11 PM]Joy: it's sort of like a runtime version of specialization
[9:12 PM]NiseVoid: So I guess disabling a component would permanently move an entity over to an archetype where that component is togglable?
[9:12 PM]Joy: yes
[9:15 PM]Joy: I mean, could do stuff like entity.take::() and then add it back, or we could add an explicit API to say "I don't want to toggle anymore" but yeah, you got it

@maniwani
Copy link
Contributor

maniwani commented Mar 17, 2024

I want to note that those Discord discussions have been about a mechanism for disabling individual components, which is kind of orthogonal / complementary to this issue's proposed Disabled marker that disables entities.

Add a special Disabled marker component. Entities with this component do not appear in any queries, unless the query contains With<Disabled> (or otherwise explicitly requests that component).

This solution described in your OP is still what I would do for disabling entities.

(edit: Like, they're both useful. entity.disable() and entity.disable_component::<T>() would make for a "temporary" version of entity.despawn() and entity.remove::<T>(), respectively.)

@alice-i-cecile alice-i-cecile added this to the 0.15 milestone Sep 18, 2024
@alice-i-cecile alice-i-cecile added S-Blocked This cannot move forward until something else changes X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers labels Sep 18, 2024
@alice-i-cecile
Copy link
Member Author

I'm merging #13120 on Monday unless someone stops me.

Once that's in, I'd like to add a Disabled component in bevy_core, along with a EntityDisablingPlugin. That plugin sohuld be part of MinimalPlugins / DefaultPlugins / HeadlessPlugins.

@alice-i-cecile alice-i-cecile added S-Needs-Design This issue requires design work to think about how it would best be accomplished and removed S-Blocked This cannot move forward until something else changes labels Sep 30, 2024
@alice-i-cecile alice-i-cecile modified the milestone: 0.15 Sep 30, 2024
@viridia
Copy link
Contributor

viridia commented Oct 1, 2024

I should note that I also have a Disabled marker component which does something quite different: it displays a button or widget in a grayed-out state. I named it Disabled because in HTML this grayed-out state is normally signified with the HTML attribute disabled, e.g. <button disabled on_click="etc...">A disabled button</button>.

@alice-i-cecile alice-i-cecile modified the milestones: 0.15, 0.16 Oct 8, 2024
@Fidius-jko
Copy link

Why not make sure that all requests have a filter: Without<Disable>?

@BenjaminBrienen
Copy link
Contributor

Why not make sure that all requests have a filter: Without<Disable>?

Are you talking about the alternative solution described in ###Custom marker component, or default query filters?

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-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers
Projects
None yet
Development

No branches or pull requests

9 participants