But if you have multiple systems (say collision detection, physics, damage/health, control(ai or player)) you can then build things up lego brick style.
For example, a basic wall will just participate in the collision detection system and nothing else, but now it's easy to add in breakable walls by giving them a hp value and defence value for the damage system.
Does that help?
The goal is to have "Systems" which operate on "Components", and "Entities" are completely out of the picture. The idea behind Systems is that they operate on a continuous block of memory:
for (auto &damagable : damagables) {
damagable.hp -= damagable.damage_this_frame;
damagable.damage_this_frame = 0;
if (damagable.hp <= 0)
damagable.dead = true;
}
for (auto &movable : movables) {
movable.position += movable.velocity_this_frame;
movable.velocity_this_frame = vec3(0);
}
Simple toy example, but by splitting up the data based on what acts on them, we have two loops that are very cache-friendly. Each of those two loops is called a "System".The System is the key part of ECS that makes this work. Just splitting off components and still using a virtual update function isn't going to get you any performance benefits, but it's still most of what I see when I see "ECS" talked about online. In fact, making components contiguous while leaving your updates to be whole-entity-at-a-time is going to make your cache coherency worse!
Entities, then, are actually not "container objects", but often just uint32's -- all of their data is inside the Components. The database analogy: The Entity is just a primary key tying together a database of tables (Components). The tables can be acted on, sometimes in parallel, by Systems (UPDATE queries), regardless of the originating Entity.
Actual Systems in practice have dependency chains and other things, to make sure that updates are done in the right order, scheduling mechanisms, and ways to make cross-component talking safe, and performant.
Unity's GameObject is not ECS, despite it being an "Entity-Component" model. Their new DOTS stack is, but it has tradeoffs for that performance.
Put simply, "EC" is a way of structuring your data classes to not rely on inheritance, "ECS" is a way of structuring your algorithms that act on those data classes to not require virtual methods.
The rest of this thread has similar misconceptions, and even the original post makes some errors too. Sadly, this misinformation is widespread, and it's not really correctable at this point. Oh well.
Actual Systems in practice have dependency chains and other things, to make sure that updates are done in the right order, scheduling mechanisms, and ways to make cross-component talking safe, and performant.
Do you know if there is a formal term for this kind of dependency chain and update order scheduling is called? I’m interested in ways this kind of checking can be done at compile time, or at least a run time guarantee that things won’t be done in the wrong order and fails to compile/generates run time errors when you have a cyclic dependency rather than having to map that all out by hand.
ECS might make this an independent-enough concern that techniques from build systems would be applicable. See "Build systems a la carte" [0], for instance, which captures the (an?) essence of build systems as managing a graph of dependent dataflow processes. (In ECS, the dataflow would be mediated by the database of component tables, just as in Make the dataflow is mediated by the filesystem.)
[0] https://www.microsoft.com/en-us/research/publication/build-s...
You need to use a programming language like C++ to be able to this. In JavaScript, all objects are pointers, so there's no way to do this.
Google "data-oriented design" to learn more.
Consider the difference between:
object 1
property A
property B
object 2
property A
property B
and: property A of object 1
property A of object 2
property B of object 1
property B of object 2
If you need the property A for all objects, in the first example we have to jump over the property Bs. In the second example all property As are next to each other.In a language where arrays are not sparse, but rather dense, the whole array is allocated as one contiguous stride of memory. Iterating through it is very fast, because the CPU cache hits every time and the next data you need is always ready, as it immediately follows the previous in memory.
Additionally, ECS wants to pack data even further, so that unneeded data is not interleaved within the memory stride at all. Every byte you "skip" is wasted space in the CPU cache, and eventually accumulates into a full cache miss, which stalls program execution.