5 ms·
Hmm, if you only have one system, then yes, those are the same. But if you have multiple systems (say collision detection, physics, damage/health, control(ai o
by Tyr42 6y ago
Hmm, if you only have one system, then yes, those are the same.
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?
- Kiro 6y agoYes, thank you! It certainly seems like a good and flexible way of structuring your game but I still don't understand where the performance benefits the article speaks of come from.
- Jasper_ 6y agoThe performance benefits comes from the "Systems", which is very infrequently talked about. Most uses of the term "ECS" are actually "Entity-Component" (EC), which has been around for a long, long time. 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.
- Pulcinella 6y agoYeah there is definitely some mental namespace pollution between EC and ECS where, as you described, you have “EC systems” (Entity-Component systems) and “ECS systems” (uhh “Entity-Component-System systems”). 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.
- Twisol 6y ago> Do you know if there is a formal term for this kind of dependency chain and update order scheduling is called? 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-systems-la-carte/ https://www.microsoft.com/en-us/research/publication/build-s...
- cma 6y agoTo go along with the parallel thing, you can imagine both of those loops are much easier to vectorize too compared to if each a was acting on objects with the damage stuff and position stuff combined in one structure.
- HelloNurse 6y agoEntities aren't out of the picture: those "damagable.damage_this_frame" and "movable.velocity_this_frame" attributes need to be assigned in a previous step, with lookups from component to entity and from entity to component. For example, a collision detection and handling step could iterate over all physical objects and, in case they collide, assign damage to the damageable component of the same entity and/or alter velocity of the movable component of the same entity (if appropriate).
- bogdanoff_2 6y agoComputers are much faster when they operate on things that are laid-out contiguously in memory, instead of jumping around following pointers. 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.
- fhools 6y agoMy understanding is the performance benefits come from the locality of the data. Usually in an array for each component. Whereas in a OOP system, your objects would be scattered throughout the heap.
- hypertele-Xii 6y agoJavaScript gains no performance benefit from ECS because JS arrays are dynamic and sparse. The performance benefit comes from dense, contiguous data (structures of arrays, instead of arrays of structures) in languages that allow that. 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.
- turndown 6y agoIn this context, what do you mean by sparse? Simply that similar data is not packed together?
- hypertele-Xii 6y agoComputer memory access is efficient when linear. That is, when the next thing you need is located directly after the last thing you needed, in memory. In JavaScript, arrays are conceptually sequential, but this is only an abstraction for the programmer. In truth, the array elements are scattered around in random memory locations. This means that when you iterate over an array, the CPU cache misses on every iteration, and the CPU has to wait for the next data to load from memory. 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.
- pharmakom 6y agoI think some JS engines may treat arrays as dense as an optimization in certain cases