My wish list for Unity3D ECS
Things I miss in Unity3D ECS compared to Entitas
I took some time (couple of hours for couple of days) to look at new and shiny ECS implementation in Unity3D. I must say, I like what I see. I also totally understand that I might be missing something, or that it is just too early and things are coming, but looking from the current state I have two (and a half) things that I miss, compared to Entitas.
First lets start with a small feature:
When working with ECS we tend to think in collections, or groups if you will.
Give me all “things” which have position (or just give me all positions). Give me all things which have position and velocity.
Sometimes however, we know that there should be at most one thing with some characteristic component. For example, imagine we have a tag component
Selected and we know that player can select only one entity.
Selected component is what we call a unique component. And by that, we make sure that only one entity in a world is allowed to contain this component. As a side effect, we get a simple ways to access such an entity.
As a matter of fact
PlayerInput in two stick shooter example project, is a good candidate to be a unique component. The game does not have a local multiplayer element, so it is safe to say that there should be only one entity with
PlayerInput component. In current implementation we can see in
EnemyShootSystem that uniqueness is implied, but it is not forced and it does not provide the simplicity and benefits that we could get by defining a component as unique.
IMHO the unique component should also provide similar benefits as the
ISharedComponentData from the memory efficiency perspective. It is basically it’s counterpart. Shared component is a single instance of a component which can be associated with multiple entities, where unique component can be associated with only one entity.
Now let’s talk about second, more complex feature:
This feature has deep implications, but the initial motivation was quite simple. We wanted to avoid having lots of tag components.
Let’s base it on an example. Imagine we have a tower defence game and we need to implement find target system. In a naive implementation, we could just go other all enemies and figure out if an enemy is close enough. However we could have a small optimisation, we could introduce a
Moved component, which indicates that an enemy just moved, so in the find target system, we could iterate only through the enemies which just moved. This optimisation reduces the set of enemies we have to iterate through.
Having tag components like
Moved can be tricky in regard to it’s life cycle.
Moved component has to be added in every system which changes/adds
Position. So we might end-up with code duplication.
Moved component might be consumed by multiple systems, this means that the removal of the tag component becomes non trivial as well.
All this hassle can be simplified, when we introduce a concept of reactive system.
A reactive system is executed only on entities which got certain component added/removed or replaced. In our small example, the target system is a reactive system, which works only on entities, which moved since last time this system was executed. The semantics of the system stay the same, but reactive system makes the
Moved tag component redundant and remove the cognitive load of managing this tag components life cycle.
Introducing the concept of reactive system, profoundly changed the way, we implemented UI and game play systems.
However there is another side to the reactive behaviour, which was an interesting side effect, based on implementation detail of reactive system.
The way we implement the reactive system is by making context, entity and groups observable. Every reactive system has a collector which gets drained when a reactive system is executed. Details can be found in my book EntitasCookBook. The interesting side effect which I mentioned before is:
If everything is observable, we can implement indexing and logging quite easily.
The idea behind the indexing is described in the Index chapter, but if you are a TL;DR type of person, here is a short summary. An Index is a custom data structure which let us query entities based on the values of the component. If we think about our tower defence example from before, index of enemies could be a quad tree, which get’s updated, every time the position of an enemy is changed.
As a matter of fact, in boids example there is a
HashPositions job, which creates a similar data structure. However as far as I understand it is built on each frame from scratch, where an index can be incrementally updated.
Now let’s talk about logging. Logging is something I am currently working on. With logging we can capture complete history of entities, when they were created / destroyed, which components they where holding and which systems were responsible for the change. This leads to complete introspection into state evolution, queryable by a small query language. Think DTrace for ECS.
Last feature which I mentioned as a half is the AnyOf matcher. I call it half of a feature, because it is IMHO just a nice to have.
In Entitas we get a group of entities by defining so called matcher. Again I would refer to the Groups chapter of EntitasCookBook, if you are interested in details. BTW the concept is based on similar feature in Artemis ECS. In Artemis it is called
AnyOf we can ask for entities, which have one of the listed components. When a system asks for entities with any of matcher, it mostly means that those components are not needed for data transformation, but rather for proper classification of an entity. Similar to the idea behind
SubtractiveComponent in Unity ECS.
I think it is a nice to have, because from my experience the use of
AnyOf matcher is limited and we can work around it by having multiple systems or introducing a new tag component, which reflects the classification we want to describe with
I am totally aware that the focus of Unity3D ECS is performance and features I listed are not that critical from performance standpoint. However I believe it is possible to implement those features with performance in mind and they would increase developer experience quite a bit, even if it is just reducing the number of tag components.
As always, I am happy to discuss all things ECS, so write some comments, or hit me up on twitter.