简体   繁体   中英

Using entity component system how to ensure at most one component exist (per entity) from a group of component types?

I'm using ecs for some time but there is a problem for which I could not find a nice, mostly general solution. There is a group of types from which at most one shall be associated to an entity. Ex. there are some trigger logic. All trigger (with the same type) can be evaluated at once in the usual parallel iteration. All works fine, but it does not make sense to have multiple trigger on a single entity. For now I have all kind of cleanup and sanity check systems(tasks) that have to be always updated when a new trigger type is created. ( Edit on update I mean the source code and not the runtime.) In the traditional oop it'd be trivial: store a single pointer per entity to a basetype, derive and implement the new rigger and that's it. In ecs they are stored in different storages and I don't have a good solution to ensure some kind of mutual exclusion on them.

Thanks for any suggestions.

I have all kind of cleanup and sanity check systems(tasks) that have to be always updated when a new trigger type is created.

...

In ecs they are stored in different storages and I don't have a good solution to ensure some kind of mutual exclusion on them.

Personally I like having the entity system notify component systems when an entity's component list changes for any particular reason. The benefit that has is that it allows systems to then be able to construct and keep an internal data structure that best suits its needs to track whatever component state is needed for fast and efficient processing during the game loop's update phase.

With that, I would create a TriggerSanityCheckSystem that maintains some data structure that tracks if an entity has an existing trigger relationship or not. If it does, this system would either throw an exception or would prohibit the addition of a new trigger component to the entity somehow. If the system detects that the entity doesn't have an existing trigger, it would register that a new one is being added, perhaps its type, and continue forward.

This TriggerSanityCheckSystem would run prior to any other trigger system to guarantee the mutual exclusivity requirement you need prior to allowing component logic to execute for triggers.

UPDATE

There may be a better approach here all together, I'm just not sure if it would fit your design. In your original question, you mentioned the following:

In the traditional oop it'd be trivial: store a single pointer per entity to a basetype, derive and implement the new rigger and that's it.

What if instead you could do that? The first problem here is you have multiple implementations of these triggers but only one of these implementations should be allowed per entity. What if instead mutual exclusivity is driven by the actual component implementation itself.

struct TriggerDelegatingComponent 
{
  BaseTrigger *delegate_;
}

The goal here is you have a single system that manages this component type and and it restricts entities having only a single instance of the component and the component restricts the logic to a single trigger implementation, nullifying the entire need for these sanity checks .

In this approach you mix the best of both worlds, ECS and OOP.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM