Some thoughts about Entity Component Systems

Hi :grinning:,

Recently, I have read about entity, components, systems and understood that they bring many advantages. It is based on a data driven approach, it is easy to serialize and different entities can be created by mixing components.

Then, I have realized that I have already been using a very similar pattern. now I am trying to figure out what differences, advantages and disadvantages are there between this and ECS.

To explain and contrast between the two, an example in ECS:

Entity (ID: 1)

-HealthComponent
-PositionComponent
-PhysicsComponent
-VisualComponent

HealthSystem - needs HealthComponent
PhysicsSystem - needs PhysicsComponent, PositionComponent
VisualSystem - needs VisualComponent, PositionComponent
             - can display HealthComponent

Entity adds HealthComponent,PositionComponent,PhysicsComponent and VisualComponent
Thus it will be managed by HealthSystem, PhysicsSystem and VisualSystem, since these query for entities that have these components.

Similar pattern, but not ECS:

Entity (ID: 1)

HealthSystem - needs HealthComponent
PhysicsSystem - needs PhysicsComponent, PositionComponent
VisualSystem - needs VisualComponent, PositionComponent
             - can display HealthComponent

Entity adds HealthSystem,PhysicsSystem,VisualSystem
Entitiy's data is the union of all necessary data requested by the systems

In the second pattern, entity adds systems instead of components. The data of entity is composed by the requirements of the systems.

Are there any advantages/disadvantages between these two patterns or are they the same? They do seems like they attempt to do the same thing but in a different manner.

Well, without details, the second approach of adding systems to an entity seems redundant. How is this association made?

In a true ES, an entity is just an ID. That’s it. The components are associated with that ID and are looked up on demand. An Entity object is just a convenient view that pulls a set of components together at that time.

Also, the whole idea behind systems is that they should operate relatively independently and on a whole bunch of entities. That’s the data-oriented approach. (Beware the term “data driven” because that’s not really what this is… “data oriented” and “data driven” mean two different things in software development.)

The classic data oriented example might be a physics integration loop. The standard OOP way would be to iterate over each object and ‘integrate’ it… ie:

public void integrate( double time ) {
    velocity += acceleration * time
    pos += velocity * time
}

The data oriented approach would be to treat it more like SIMD style operations in two passes.

for( int i to object count ) {
    velocity[i] *= acceleration[i] * time;
}

for( int i to object count ) {
    pos[i] *= velocity[i] * time;
}

At the native code level (if one were writing games in native code on different platforms) this has advantages in platforms with strict memory streaming requirements as all data can be organized efficiently.

…but even outside of that, this decoupling (and the general mindset behind it) provides many design advantages.

2 Likes

P.S.: In case it wasn’t obvious, I’m the author of the JME friendly ES library Zay-ES.

Lightweight, “pure”, supports persistence and networking, etc…

Thank you for your reply. Yes, I am aware and have read the wiki of Zay-ES. :slight_smile:

In the case the second approach was implemented the same way as a true ES. Then the action of ‘adding’ systems to entity would equal to adding the union of components the systems require.

Otherwise, entity could still be an ID. And components(eg classes, or primitive fields) are associated with it, not by user, but by the system that it ‘adds’. In this way, it could also be that a system ‘adds’ an entity; possibly keeps a list of entities instead of querying. Do you think this approach loses any advantages of a true ES?

Now that I think about it. In an ECS, An entity associates with Components. Then System queries entities with certain components, which it can cache if they don’t change. When component is removed from Entity, a system has to query again. Is this how ECS work?

In the second approach. Entity is associated with Systems first and then data. Systems do not query as they are associated with entity(eg. system has list of its entities). When entity removes a System, the data associated with that system, if not shared by other entities’ system, is dropped. Thus, one advantage of this approach is there is no querying; better performance!

Does this makes sense? Then again, I wonder what are the disadvantages I am currently not seeing.

PS:
In the second approach I have been assuming that one System acts on the same Set of Entities. If this were true for the first approach as well, then it would be too possible to remove the need for quering, thus the two being the same again.

Yeah, it adds tighter coupling of components and loses some flexibility. Plus, now you have to trick extra stuff when systems overlap in their component usage… and 9 times out of 10, it’s systems adding components that represent their results and don’t even necessarily get added until some later time.

For example, I have a ModelSystem that watches for Position and ModelInfo. I have a PhysicsSystem that watches for Position and Velocity. We overlap on the need for Position. I see no reason to add a ModelSystem and a PhysicsSystem to an entity. It’s adding a correlation that is 100% unnecessary. Neither of those systems cares about each other. They only care about entities that have the components that they want… and that state can (and definitely will) change a lot over the course of an entity’s life cycle.

To continue the example, maybe the entity goes to sleep and so the Velocity gets removed and now the PhysicsSystem can ignore it.

Your approach means that whatever is removing that Velocity component would actually have to know all possible systems that might be using that Velocity component and remove them all! Yuck. Might as well go back to OOP really because everything is suddenly tightly coupled again.

The “requerying” thing is not really relevant. It’s possible to optimize this under the covers… for example, Zay-ES never requeries for these normal operations as an EntitySet is a view and keeps track of external changes. applyChanges() simply updates what it already knows to new state and then removal is just a side-effect.

Even if you were building a federated back-end across multiple computers, each running one of your systems and communicating with a database, your solution offers no advantages because now your systems have to be in each others business all the time.

The bottom line: keep your systems as decoupled as you possibly can and you open up all kinds of cool design opportunities you never would have thought about with careless coupling.

1 Like

Thank you for your explanation. Especially for that example with the Entity with Position and Velocity. Yes, it definitely is more intuitive and maintainable to make an Entitiy add Position and Velocity instead of ModelSystem and PhysicsSystem. It’s even hard to think and reason about what the latter accomplishes.

I think now I got a much clearer picture of the ECS. Thank You. :chimpanzee_amused: