Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Don't forget to read Tuesday's email newsletter for your chance to win a free copy of Construct 2!


Like
12Likes
Dislike

A Critique of the Entity Component Model

By Sean "rioki" Farrell | Published Apr 03 2014 06:36 AM in Game Programming
Peer Reviewed by (Servant of the Lord, Hodgman, dejaime)

programing design entity

The entity component model is all the rave for the last decade. But if you look at the design it opens more issues than it solves.

Broadly speaking the entity component model can be summarized as the following: An entity is composed of components who each serve a unique purpose. The entity does not contain any logic of its own but is empowered by said components.

Attached Image: ecm-intro.png

But what does the entity component model try to solve?

Each and every illustration of the entity component model starts with a classic object oriented conundrum: a deep inheritance hierarchy with orthogonal proposes. For example you have a Goblin class. You form two specialisations of the Goblin class, the FylingGoblin who can fly and the MagicGoblin who uses magic spells. The problem is that it is hard to create the FylingMagicGoblin.


Attached Image: dreaded-diamond.png


Even with C++ and its multiple inheritance, you are not in the clear as you still have the dreaded diamond and virtual inheritance to solve. But most languages simply do not support a concise way to implement it.

When solving the issue with components, the components GroundMovement, FlyingMovment, MeleeAttack and MagicAttack are created and the different types of goblins are composed from these.


Attached Image: flying-magic-goblin.png


Good job, now we went from one anti-pattern (deep inheritance hierarchy) to a different anti-pattern (wide inheritance hierarchy). The central issue is that the inheritance hierarchy tries to incorporate orthogonal concepts and that is never a good idea. Why not have two object hierarchies, one for attack modes and one for movement modes?


Attached Image: goblin-sanity.png


As you can see from an object oriented standpoint the entity component model fares quite poorly. But that is not the only problem the entity component model tries to solve.

In many cases you see the concept of a data driven engine. The idea is that you can cut down on development time by moving the object composition out of the code and into some form of data. This allows game designers to "build" new objects by writing some XML or using a fancy editor. Although the underlying motivation is valid, it does not need to use an entity component model, as a few counter examples show quite well.

Putting the structural criticism aside, a naive implementation of the entity component model can in no way be efficient. In most cases the components are not such high concepts as moving or attacking, they are more along the lines of rendering and collision detection. But unless you have additional management structures, you need to look at each and every entity and check if it has components that are relevant for the rendering process.


Attached Image: inefficient.png


The simplest way to resolve the issue, without altering the design too radically, is the introduction of systems. In this case the actual implementation is within the systems and the components are just indicating the desired behaviour. The result is that a system has all the relevant data in a very concise and compact format and as a result can operate quite efficiently.


Attached Image: better-graphics.png


But if you look closely you can see that we have all these useless components. What if you removed the components and just used properties on the components and the systems just look for appropriately named properties? Now you have duck typing.


Attached Image: duck-typing.png


Duck typing is a concept that is used a lot in weakly typed languages, like for example JavaScript or Python. The main idea here is that the actual type is irrelevant, but specific properties and function are expected on a given object within a specific context. For example it is irrelevant if it is a file stream, a memory stream or a network socket - if it has the write function it can be used to serialize objects to.

The problem with duck typing is that is does not lend itself easily to native languages. You can cook up some solution using some varying type but in no way is this an elegant solution.

Chances are you already have a scripting system, in this case the solution is quite straight forward, you implement the core game logic in scripts and underlying systems look at this definition and implement any heavy lifting in native code. The idea of alternating hard and soft layers is nothing new and should be considered where flexibility and performance is needed.

You may think that implementing the game logic in scripts is an inefficient way to do it. In cases where you are building a simulation-oriented game this may be quite true. In these cases is makes sense to extract your logic and reduce it to its core concepts, a simulaiton if you will. Then implement the presentation layer and control layers externally directly against the simulation layer.


Attached Image: totally-not-mvc.png


The nice thing about this design is that you can split the presentation layer and simulation so far that you can put one of them on one computer and the other on a different one.


Attached Image: mvc-network.png


Wait, did you just describe MVC? Um... No... Stop changing the subject.

When looking into scalability you get interesting requirements. One very clever implementation of the entity component system was make it scale in an MMO setting. The idea here is that entities and components do not exist in code, but are entries in a database. The systems are distributed over multiple computers and each work at their own pace and read and write to the dabase as required.


Attached Image: mmo-ecm.png


This design addresses the need of a distributed system and reminds me of the High Level Architecture][10] used by NASA and NATO to hook up multiple real-time simulations together. Yes this design approach even has its own standard, the IEEE 1516.

Ok, oh wise one, what should we do?

If you read about these issues you are either building a game engine or a game. Each and every game has different requirements and game engines try to satisfy a subset of these different requirements. Remember what you are developing and the scope of your requirements. If your current design sucks, you do not need to go overboard with your design, because chances are you aren't gonna need it. Try to make the smallest step that will solve the problem you are facing. It pays out to be creative and look at what others have done.



About the Author(s)


Sean "rioki" Farrell is a professional software developer with a hobby of developing games.

License


GDOL (Gamedev.net Open License)




Comments

My gripes with ECS have to do specifically with the lack of solid documentation, all the documents and blog posts I've read so far are too abstract and some leave important concepts just mentioned as if they were obvious to everyone. It would be nice to have an Article-Tutorial going over the concepts with actual code intertwined. I have started coding a proof of concept "ECS Framework" over at github, so perhaps I may take that endeavor myself in the future.

 

One thing that I realized is flimsy about ECS is the idea of less cache misses due to keeping components contiguously in memory, as phil_t mentioned, this IMO is only valid if you have one system one component, and systems are the ones managing their components use of memory, but it doesn't take too long to realize that both the rendering system as well as the physics system will require access to the position component, so who is the owner of the component? the physics system may require access to other components such as a velocity component for example, so you'll be referencing at least 2 different arrays of components for one system. The naive approach of keeping position on multiple components, say a rendering component and a physics component just complicates things as now you have to keep all of them in sync, loosing one of ECS features, independence between systems.

 

This, I think requires more though, I could see how interleaved component arrays (keeping heterogeneous components for a single entity together) could avoid cache misses, but then you'll have to iterate systems over entities rather than components over systems... which I don't really think is such a bad idea, unless you do want to go with the original concept of an entity being no class or struct at all but rather just a primary key in a SQL Select statement.

 

I am also glad to see an article that is questioning entity component systems. I am curious why people say it is entity-component vs OOP since and entity component system is a design pattern that uses OOP.

 

I am not 100% certain about all of the critiques but my take away from the article is don't waste your time trying to build a system like everyone else uses. Build a system that gets the job done. Whether it is 'OOP' or ECS, build what you need to finish your game.

 

technically, OOP is not required for implementation of a C-E system.

 

 

That is true, but it helps. 

My gripes with ECS have to do specifically with the lack of solid documentation, all the documents and blog posts I've read so far are too abstract and some leave important concepts just mentioned as if they were obvious to everyone. It would be nice to have an Article-Tutorial going over the concepts with actual code intertwined. I have started coding a proof of concept "ECS Framework" over at github, so perhaps I may take that endeavor myself in the future.

 

One thing that I realized is flimsy about ECS is the idea of less cache misses due to keeping components contiguously in memory, as phil_t mentioned, this IMO is only valid if you have one system one component, and systems are the ones managing their components use of memory, but it doesn't take too long to realize that both the rendering system as well as the physics system will require access to the position component, so who is the owner of the component? the physics system may require access to other components such as a velocity component for example, so you'll be referencing at least 2 different arrays of components for one system. The naive approach of keeping position on multiple components, say a rendering component and a physics component just complicates things as now you have to keep all of them in sync, loosing one of ECS features, independence between systems.

 

This, I think requires more though, I could see how interleaved component arrays (keeping heterogeneous components for a single entity together) could avoid cache misses, but then you'll have to iterate systems over entities rather than components over systems... which I don't really think is such a bad idea, unless you do want to go with the original concept of an entity being no class or struct at all but rather just a primary key in a SQL Select statement.

 

1.) Lack of solid documentation is mainly because it is a pattern, idea abstracted away from its original use-case. But keep in mind that ECS can by used to solve several problems in game / game engines. It maybe a solution to a question "How designers can create new entities without help of programmers? / How to let designers easily to modify different properties/attributes of certain entities?" or it might be a solution for problem "How to create re-usable game logic and avoid OOP inheritance problems?".

 

First question is more visible in Unity 3D, it exposes entity construction to designers with simple UI. You can easily create new types of entities just by adding few components. With build-in components you can get far, but gets more complex when you want to create custom components.

 

If you create such solution by yourself you need to pay attention how you can easily create new components with editor. One solution might be defining a metadata to describe component attributes (keys, names, descriptions, types, mix & max values, default values etc.) or you can go with fully DB backed up solution. Once this is "solved" you need to figure out that do you want to simulate game inside your editor, if yes, you need to figure out how you can make editor to run that custom system that you just created. You could easily compile new engine (dynamic lib) for editor, or you could decide that editor is just a DB/entity browser and it will communicate with your game binary via TCP/IP. You might also get a request to create prefab system to aid level designers and ton of other things...

 

Second question is different beast. First question was more of how to create and pass data to engine's subsystems and it normally follows subsystems' features. But in logic / behaviour component side it is all about logic and of course ton of variables to fine tune game play. Some prefer doing this with scripting language so you have ScriptComponent or similar in our engine side and this component is exposed via editor UI. ScriptComponent points to some external script assets which has the actual logic, and usually has some extra attributes for engine. In side this script asset you then have your logic, and depending you choices you can have another ECS system implemented with script language and dedicated to make logic scripting more easy. If are planning to go without scripting language then ECS behaviour system is more vital to you, it helps you to create logic with languages like C++. It is also more vital since you most probably want to expose all possible variables to editor UI to make those accessible by designers. Depending what you are doing you will most likely need a message/event system between different system,  system are depending on each other more heavily etc. But this is game implementation side of things, so it can be messy and it certainly will, but having ECS in place you will have some changes to create re-usable logic components and debugging should be more easier.

 

2.) I would say that it isn't naive approach to duplicate e.g. position, orientation and scale for rendering and physics components. Those are two different "world", so it is pretty much how it should go anyway. What you really need is a message/event "system" that can be used to sync data between different components. It can be something sophisticated or you can design our engine's update loop in a way that you can pass updated cache friendly continuous data blocks from system to system. Duplication also helps you to make your system concurrent, each task/job can run pretty much parallel without any problems if you keep dependencies between system low.

 

 

I am also glad to see an article that is questioning entity component systems. I am curious why people say it is entity-component vs OOP since and entity component system is a design pattern that uses OOP.

 

I am not 100% certain about all of the critiques but my take away from the article is don't waste your time trying to build a system like everyone else uses. Build a system that gets the job done. Whether it is 'OOP' or ECS, build what you need to finish your game.

 

technically, OOP is not required for implementation of a C-E system.

 

 

That is true, but it helps. 

 

 

I think the argument was more against deep inheritance trees, not OOP entirety. It is probably still a good idea to implement you C-E system with objects, but you are avoiding deep inheritance in favour of aggregation.

 

As other people have said, do whatever works best for your game. For me, the entity systems reduces the complexity and dependencies the entities have on each other.

1.) Lack of solid documentation is mainly because it is a pattern, idea abstracted away from its original use-case. But keep in mind that ECS can by used to solve several problems in game / game engines. It maybe a solution to a question "How designers can create new entities without help of programmers? / How to let designers easily to modify different properties/attributes of certain entities?" or it might be a solution for problem "How to create re-usable game logic and avoid OOP inheritance problems?".
 
First question is more visible in Unity 3D, it exposes entity construction to designers with simple UI. You can easily create new types of entities just by adding few components. With build-in components you can get far, but gets more complex when you want to create custom components.
 
If you create such solution by yourself you need to pay attention how you can easily create new components with editor. One solution might be defining a metadata to describe component attributes (keys, names, descriptions, types, mix & max values, default values etc.) or you can go with fully DB backed up solution. Once this is "solved" you need to figure out that do you want to simulate game inside your editor, if yes, you need to figure out how you can make editor to run that custom system that you just created. You could easily compile new engine (dynamic lib) for editor, or you could decide that editor is just a DB/entity browser and it will communicate with your game binary via TCP/IP. You might also get a request to create prefab system to aid level designers and ton of other things...
 
Second question is different beast. First question was more of how to create and pass data to engine's subsystems and it normally follows subsystems' features. But in logic / behaviour component side it is all about logic and of course ton of variables to fine tune game play. Some prefer doing this with scripting language so you have ScriptComponent or similar in our engine side and this component is exposed via editor UI. ScriptComponent points to some external script assets which has the actual logic, and usually has some extra attributes for engine. In side this script asset you then have your logic, and depending you choices you can have another ECS system implemented with script language and dedicated to make logic scripting more easy. If are planning to go without scripting language then ECS behaviour system is more vital to you, it helps you to create logic with languages like C++. It is also more vital since you most probably want to expose all possible variables to editor UI to make those accessible by designers. Depending what you are doing you will most likely need a message/event system between different system,  system are depending on each other more heavily etc. But this is game implementation side of things, so it can be messy and it certainly will, but having ECS in place you will have some changes to create re-usable logic components and debugging should be more easier.
 
2.) I would say that it isn't naive approach to duplicate e.g. position, orientation and scale for rendering and physics components. Those are two different "world", so it is pretty much how it should go anyway. What you really need is a message/event "system" that can be used to sync data between different components. It can be something sophisticated or you can design our engine's update loop in a way that you can pass updated cache friendly continuous data blocks from system to system. Duplication also helps you to make your system concurrent, each task/job can run pretty much parallel without any problems if you keep dependencies between system low.


1. I would say it is closer to a paradigm than a pattern, you can usually describe a pattern with a class diagram solidifying the abstract idea, with ECS, you have to mix and match concepts depending on your needs/tastes, I don't disagree with you, I just feel that what you mentioned is a sign of the idea's novelty, as it matures, a more specific approach should surface.

2. I feel that if you have to keep data in sync between 2 different components/systems, you are in part defeating the purpose of keeping systems separated and independent from one another, might as well go back to the old ways, it would be less complicated. Also I don't see how rendering position and physics position belong to different worlds, OK, physics position may be the center of gravity, while rendering position may be the mesh origin, if you keep the physics position as an offset from the rendering position, or the other way around you don't have to sync them at all.

First, I have to say when people say they don't like/support OOP yet they use an Entity-Component model I have to roll my eyes.

The Entity-Component model concept is an application of GoF software architecture patterns, rather than something used in place of OOP really.

OOP gets a bad rep from reckless and lazy design practices we've all seen; mixing of core behavior or interfaces with concrete implementations, confusing options with operands, failure to understand the problem domain, etc.

The Entity-Component model you can say is built from the Iterator, Composite, Decorator, Command & Strategy patterns in the classic GoF OOP patterns.

The terms 'Entity' and 'Component' themselves are still very broad in terms of implementations; sharing some basic features in common, yet different approaches around a general assemblage of patterns.  

There's probably as many variations in expression of the model as there are development teams applying such a model.  None of the perfect, of that we can probably be sure.

Also a MVC pattern is not necessarily mutually exclusive to such an approach; Model (Data/Domain Model), View (Representation) and Controller (Behavior) really being about separation of concerns into bounded logical contexts vs. concrete implementation.

In the end we really just create abstractions that help to make the whole thing managable and deliver on our needs.  So ideally we mix the best-of-breed approaches to suit the individual challenges those needs create in a way we can cope with.

Just an addition: You used Python as an exampe of weakly typed language. While I agree that it is not the strongest typed language out there, it's arguably still a strongly typed language, albeit a dynamic one. For example, it will not let you add a string to an int.

One thing that I realized is flimsy about ECS is the idea of less cache misses due to keeping components contiguously in memory, as phil_t mentioned, this IMO is only valid if you have one system one component, and systems are the ones managing their components use of memory, but it doesn't take too long to realize that both the rendering system as well as the physics system will require access to the position component, so who is the owner of the component? the physics system may require access to other components such as a velocity component for example, so you'll be referencing at least 2 different arrays of components for one system. The naive approach of keeping position on multiple components, say a rendering component and a physics component just complicates things as now you have to keep all of them in sync, loosing one of ECS features, independence between systems.

I think you are massively overstating this problem.

Are your components really so light on data that you can fit multiple different components in a single cache line? A single 4x4 float matrix takes up an entire cache line on most systems, so I strongly doubt it.

Realistically, you want to go even further with the 'outboard' nature of the ECS design if you want to solve cache coherency issues. Establish a single, stand-alone array of position data, which is traversed by both the physics and render systems in turn.

 

One thing that I realized is flimsy about ECS is the idea of less cache misses due to keeping components contiguously in memory, as phil_t mentioned, this IMO is only valid if you have one system one component, and systems are the ones managing their components use of memory, but it doesn't take too long to realize that both the rendering system as well as the physics system will require access to the position component, so who is the owner of the component? the physics system may require access to other components such as a velocity component for example, so you'll be referencing at least 2 different arrays of components for one system. The naive approach of keeping position on multiple components, say a rendering component and a physics component just complicates things as now you have to keep all of them in sync, loosing one of ECS features, independence between systems.

I think you are massively overstating this problem.

Are your components really so light on data that you can fit multiple different components in a single cache line? A single 4x4 float matrix takes up an entire cache line on most systems, so I strongly doubt it.

Realistically, you want to go even further with the 'outboard' nature of the ECS design if you want to solve cache coherency issues. Establish a single, stand-alone array of position data, which is traversed by both the physics and render systems in turn.

 

 

Even if you can't keep the data in a single cache line, there's still a fairly significant performance hit for having non-contiguous memory reads.

  • Thanks to eager pre-fetching on most modern hardware, a failure to align or fully utilize those cache lines will add refetching and will waste cycles.  
  • Taking into account row-major ordering in most languages (C/C++, Pascal, Python, etc.) simple code may run four times slower traversing columns in an array vs. rows.

When it comes to memory management always keep these facts in mind:

  • Bandwith - The memory bus is limited, abuse limits scalablity.
  • Latency - Irregular access patterns cause cache misses, which causes stalling.
  • Locality - Unused data in caches not only waste cache, but also memory bandwidth.
  • Contention - Multi-core CPUs and threads contending over data in caches causes stalls.

Generally you'll get a noticable boost in performance out of well-aligned contiguous memory; e.g. plain-old object arrays.

Different needs and limitations faced by different sub-systems require different solutions, you blend in what works best for each challenge to get the best results.

In other words; our model the game development takes place against is just a facade that's comfortable to work with and is abstracted from the engine's underlying data and memory management details, which are all about getting the best performance out of the hardware.


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS