I think there's some confusion about the precise meaning of "ECS" or "Entity Component System" as applied in game design and potentially other applications. Even the first two sentences of the Wikipedia article don't agree on whether it's a countable or uncountable noun:
Entity component system (ECS) is a software architectural pattern. An ECS consists [...]You wouldn't say "An OOP consists of ..."
So here's my taxonomy of the several meanings of "ECS" and other things it's easily confused with. Although these things are quite strongly overlapping and intertwined, I hope to tease them apart a little.
Software can be written in an architectural style with entities, components, and systems — whether they are called that, or not.
ECS says that your game world (this is an architecture specific to games) is made of entities, but the entities in the game world aren't necessarily reified in the game's code
(there is no single Entity structure). Instead, an entity results from the combination of different aspects, such as its 3D model, health bar, and physics box.
It likewise says that the game code can be divided into different modules, or systems, such as rendering, combat, and physics.
If you've ever used a third-party physics library for your game, you've probably had to create a "physics object" corresponding to each game object that has physics.
The physics library takes care of simulating the physics for all the physics objects, and once per frame, you copy their positions and rotation back to the game objects
for rendering — quite a weak connection. At best, your struct Entity contains a pointer to a physics object.
This decoupling and modular self-containment is exemplary ECS style. The physics object is a component and the physics library is a system. You might also have it in other parts of the game such as networking, though I think physics is the most common.
ECS architectural style doesn't imply a certain way of storing data in memory. There are many different data structures that implement this style.
EnTT is one library that identifies itself as "an entity-component system". By looking at the examples, it's clear that it manages entities and components, but it doesn't manage systems, so it is an entity/component system or (if you hate clear communication) "an ECS". We could instead call it an "EC library" or an "EC framework" to reduce ambiguity.
Some would argue that the systems in ECS architecture shouldn't be managed by a library, so every EC library is also an ECS library, as the S part is a no-op.
The "system" in "entity/component system" is emphatically not the same "system" in "entity/component/system architecture".
You could also call it an "entity component system", as it is a system that manages components of entities (entity components) but that is the exact same architecture so it's a distinction without a difference. You can't have entity components unless you also have entities. A system that manages entity components necessarily manages entities as well.
Now it really gets confusing. Some EC systems also have facilities for managing systems, which makes them "entity/component/system systems" or "ECS systems". The word "ECS" sometimes means one of these.
For example, EntityX is similar in concept to EnTT, but also manages systems. It calls itself "an Entity Component System" and doesn't even try to clarify what that means at all.
struct Game {
vector<vec2> position;
vector<vec2> velocity;
vector<float> health;
};
instead of the more common Array of Structures:
struct Entity {
vec2 position;
vec2 velocity;
float health;
};
struct Game {
vector<Entity> entities;
};
SoA more efficiently uses the CPU cache when performing the same operation on many entities (such as position += velocity*timestep;).
It gets confused with ECS (any of them) because the possibility of using SoA is one of the benefits of ECS architecture. While OOP architecture typically forces you to have an array of pointers to structures, ECS architecture admits more implementation variety, including SoA.
However, they are not the same thing. The example above is SoA, but it's not ECS because there are no components. The following "megastruct design" breaks up the entity into components, but doesn't use SoA:
struct Motion {
vec2 position;
vec2 velocity;
};
struct Combat {
float health;
};
struct Entity {
optional<Motion> motion;
optional<Combat> combat;
};
struct Game {
vector<Entity> entities;
};
Data-Oriented Design is often confused with SoA, because in practice, a lot of it boils down to using SoA and few big loops.
However, DoD is a higher layer of abstraction. DoD is the practice of thinking about your program in terms of the data that flows through it, rather than, say, world modeling or code structure. It happens that on modern CPUs (and even in 2009, when the term came about) running video games, the efficient way to process data is usually with big loops over homogeneous arrays with small elements, but this is not always the case.
On processors from the 80s and 90s, for instance, linked lists were quite efficient. These processors had no caches or data prefetchers— every byte of memory access cost the same amount. A linked list allows you to jump instantly to the next item without extra lookups. When you're writing code for such an old processor, or even a modern tiny microcontroller with no cache, DoD thinking may lead you to use a lot of linked lists.
And on giant server clusters, DoD thinking may lead you towards MapReduce.
Here are some other illustrative examples of data-oriented thinking are:
foreach(Entity e) e.pos += e.vel * timestep; rather than thinking of each entity moving itself separately.