My understanding (having not implemented or heavily used ECSs myself) is that the two main benefits of them are:
It seems like there might also be the benefit of not having to manage/coordinate weird inheritance hierarchies that can cause devs to step on each others’ toes.
That said, given modern hardware, ECS is certainly not the only way of solving these problems. Lots of big games don’t use this approach.
For everyone in the thread: do you think ECS is suitable for general application programming?
The nice part about ECS for games is that you can lock up all the state for, say, a movement system in a single module. In app programming, we tend to throw all of the components together and hope that they remain intelligible. So you might have a completely unrelated system making decisions based on the data from other systems and it is very difficult to trace that.
My question too. For all the stuff I’ve seen about how great this and “data oriented design” are, I have yet to see anything about them that isn’t about 3D games, or very similar types of 3D graphics applications. My take so far is that these are great ways to optimize when you have zillions of tiny simple pieces of numeric data to deal with, but I’m not seeing the appeal for anything that I ever work on, like databases or network protocols or languages or social software…
A solution to the problem said to exist with composition is to make variables context local, e.g. in Godot, position refers to relative position to the parent node, so it is clear where to handle it. Also, I think that it’s worth pointing out that ECS doesn’t remove dependencies, it just obscures them by removing them from the type system, since you will still inevitably have some systems depending on the behavior of other systems, but now the dependency isn’t represented in the type system.
Can you elaborate? The parent post doesn’t make it clear, but entity in ECS normally is just an int64, I guess that’s what you mean? But the dependencies (and type) should be expressed as dependencies between systems in ECS, not on either entity or components?
Hmmm, if the entity is just an id, then how does one keep track of which components are registered to an entity? Or is it usually the other way around, a component knows which entities it owns?
EDIT: Looks like the answer to my second quesiton is yes based on http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html
With ECS there isn’t a good way to express that one system depends on the fact that the same entities must be processed by another system. The dependency is still there, but now it has no representation in type systems.
If you think ECS systems are about “problems with OOP” and not “cache coherence” I’ve got a bridge to sell you.
Composition over inheritance. Maintainability. Decoupling.
The cache lcoality is to me a nice bonus.
The author presents two problems with oop (1. multiple inheritance and 2. the question of which object should own mutations of common properties). These are similar arguments to those made by another game developer of note https://kyren.github.io/2018/09/14/rustconf-talk.html (chucklefish), which lends credence to the ideas.
IIRC chucklefish also discusses cache coherence, but on balance ECS appears to help with both cache and OOP problems. It’s not exclusive.
Would you care to give more than a snarky response?
There’s a topic at the Perl conference later today about this:
@endershadow8 thanks for writing this!
It would be great if you could spend a bit more time in the article giving us a taste of what coding for an ECS looks and ‘feels’ like.
For instance, what kinds of data structures are entitites and components commonly implemented in terms of? How does persistence work? What’s the API like?
My intention for this article was only to try to explore some of the lines of reasoning that ECS is derived from. I’m planning to write something more detailed in the future.
Ah OK I totally get it. I look forward to future articles in the series!
I also think I understand a bit better what ECS is supposed to be. It’s not about any particular implementation, because entities and components could be provided by whatever framework you’re using, a database, etc etc. It’s just a way of organization information that avoids the pitfalls of object systems.
(composition) Where would the function updatePosition live?
(composition) Where would the function updatePosition live?
Isn’t this an easy question to answer if one knows about how composition is implemented through either delegation or forwarding? I can certainly see how it can be problematic if one can’t actually implement composition in code, but should the paradigm or the user be considered at fault here?
I’ve always wondered how far back this pattern goes. We’re people on the PS1 writing code in ECS style? It seems pretty heavy as a model of organizing your system, in universes where you only have a couple megabytes of RAM
I wouldn’t be surprised if people were writing PS1 code in something that wasn’t too far divorced from ECS style. A lot of old code uses arrays of entities.
A programming model’s mental weight rarely correlates to it’s performance and memory characteristics.
That being said, I write games (when I do), in Godot, which doesn’t expose ECS to the user (though I think they get some of the benefits from processing things in tight loops inside the engine).
One of the more famous early examples of ECS was quake. I don’t know if it originated ECS, but it was certainly in use by 1996.
Given that an ECS is basically just arrays of structs, I’d say it’s been around since the dawn of time.
ECS can be very memory efficient, since all the data is stored contiguously without padding. For some sparse relationships you use hash maps, but they’re not worse than overhead of a memory allocator.
However, I don’t think ECS was a thing on PS1: OOP was hot back then, and I don’t think ECS even had a name yet.
ECS started to really matter later when CPU cache started dominating performance more than anything, and when multicore made objects referencing random other objects untenable for thread safety.