Hacker News new | past | comments | ask | show | jobs | submit login
A Simple Entity Component System (2019) (austinmorlan.com)
66 points by lordleft on Aug 31, 2022 | hide | past | favorite | 35 comments



IMO, ECS is a better way of doing OO (even on a non-OO language) than OO languages themselves:

- Avoid the inheritance vs. composition issue

- Entity as a first-class concept (no more Object vs. "Just Value Object", confusion regarding equality of objects, object hash methods)

- System as a first-class concept (not just a consequence of a call stack between multiple inter-dependent objects)

- It serves much of the same purpose of the Flyweight Pattern

- Naturally conductive of functional programming

This particular implementation is interesting, it introduces some additional pieces (Component Manager, System Manager).


I've read up on ECS thrice now, (but never used it because I don't do games) and I really WANT to like it, and I feel it does have value in other domains.

But... I realize every time that I forget what an entity, component and system really "is" (i mean to be fair the naming is almost satire of genericness), and the abstraction doesn't stick to my brain in a way that I had hoped. Kinda similar to how monads don't stick, and fades away over time (if you're not doing FP). Usually, this is a red flag for abstractions, but perhaps the material I've read has been poorly written. Does this feeling go away when you're used to it? Does it just feel natural and obvious after a while?


You need to use it to have it stick. If you like Rust, spend 30min playing with bevy and you’ll be set.


That's a really interesting take. I'd love to see an experimental language based around this approach because I think it could really push your idea much further than a library.


One of the core tenets of OO is encapsulation. ECS rightly treats encapsulation as a bad thing, because data (i.e. entities) should not be encapsulated in the components and systems which operate on them.


I'd say that encapsulation may be problematic because it's essentially hidden mutable state, which makes reasoning about systems hard(er).

An attempt to hide private pieces of data may be counter-productive when an entity system tries to globally optimize the handling of state. A lack of protection though may result in inadvertently depending on parts of data that are a fleeting implementation detail; tight coupling has its own problems.


This article makes a convincing argument that ECS is not necessarily as opposed to OOP as some proponents would have you believe:

https://www.gamedev.net/blogs/entry/2265481-oop-is-dead-long...

> I've been a long-time ranter in many "ECS" threads on the forum, partly because I don't think it deserves to exist as a term (spoiler: it's just a an ad-hoc version of the relational model), but because almost every single blog, presentation, or article that promotes the "ECS" pattern follows the same structure:

> 1. Show some terrible OOP code, which has a terribly flawed design based on an over-use of inheritance (and incidentally, a design that breaks many OOD rules).

> 2. Show that composition is a better solution than inheritance (and don't mention that OOD actually teaches this same lesson).

> 3. Show that the relational model is a great fit for games (but call it "ECS").

> This structure grinds my gears because:

> (A) it's a straw-man argument.. it's apples to oranges (bad code vs good code)... which just feels dishonest, even if it's unintentional and not actually required to show that your new architecture is good, but more importantly:

> (B) it has the side effect of suppressing knowledge and unintentionally discouraging readers from interacting with half a century of existing research.

Sorry for the lengthy quote, but I reckon this article is a classic.


I have a similar feeling, but have not converted that feeling into building production code that way yet, partially because it conflicts with a large amount of existing code that I work on in terms of design. It definitely has tradeoffs, but I found a large class of problems went away when I used it.


ECS is a one of the possible designs of what we usually call "the gamestate" that is the dynamic structure that holds the game world simulation.

But the origin of ECS is Data Oriented Design, also known as DOD, that emerged as a reaction to the bad properties of OOP.

DOD is more general and IMHO more interesting than ECS.

I have not yet seen a satisfying implementation of ECS, it can be extremely fast compared to OOP approach, but also cumbersome when dealing with dependencies between systems and the ways to handle structural changes.

My take would be : learn the principles, do some benchmark, but do not consider any ECS implementation as gospel, this is not mature yet.

Use the ideas and principles to build a fast simulation in your own way.


I agree - I have looked into a 2 or ECS implementations, and they either seemed awkward, or brought in a bunch of other concerns I did not care about, and in the end decided to roll my own for some games I was building. It was pretty simple in the end. Dependencies definitely became a bit lengthy in some cases, but I didn't hate that, in that it makes the coupling explicit.


I think that systems should be updated in a very explicit order, so that dependencies are easier to manage.

And for the multithreaded usage, a parallel_for can be used to update systems.


Do you have good resources on DOD?


The horse is thoroughly out of the barn on this one, but "an Entity Component System" is a misnomer similar to referring to "a Model View Controller". Sometimes it's written as "Entity-Component-System" to clarify that "entities", "components", and "systems" are all concepts within the ECS architecture.


Part of the reason for the confusion is that this is a retcon of sorts. ECS was first described (with a different name) in a postmortem of Dungeon Siege, but as far as I'm aware, the modern usage of "system" was coined by Adam Martin during the development of Operation Flashpoint: Dragon Rising.

There are good technical arguments for making systems into first-class values, of course, but this interpretation will always be in competition with the older one.


Interesting. The reason I find it worth pointing out is that the "entity" and "component" parts are not really that interesting by themselves. You could have an object-oriented "entity-component" system where components encapsulate game logic, for instance. It's the separation of game logic into the separate "systems" that is interesting. That said, the actual implementation of "systems" varies so much that it's not surprising people focus on the "entity" and "component" parts.


I would love to see some exploration of the ECS pattern being used for web development. A couple years ago I wrote an experiment[0] using ECS with Web Components to make a simple calculator, and... it was actually pretty nice. ECS does a great job of flattening nested structures, and would be really curious if this would improve things like prop drilling in React.

0: https://brochington.github.io/ecstatic-doc-site/docs/example...


Fundamentally, UI is nested. The framework has to reflect this and make UI components composable in a bested way or you’ll drive developers crazy.

What I do wonder about is being able to use a data oriented system that does reflect this neat ability. E.g. if the data in an ECS system is viewed as a simplified relational database, what would happen if we replaced it with a simplified graph database?


Isn't a graph of objects a simplified graph database?

Trees are not arbitrary graphs; a tree-based database may have nicer propertied than a web of objects, while allowing to describe nesting in a natural way.


That's certainly an interesting way to organize things, though I dislike the reliance on several 'manager/coordinator' classes. (though part of this is my superstition that class names shouldn't end in -er or -or)

The main mechanism seems to be for 'systems' being able to iterate over tuples of 'components' of a specific type for all 'entities'. I can't help but wonder if there isn't a more elegant way of doing this than trying to keep the set of entities for each system up to date. Though I suppose it depends on what you want to optimize.


One of the main goals of ECS is to improve the use of the cache and reduce the amount of dereferencing.

An alternative approach, which does not sacrifice the more common OOP style: https://www.doc.ic.ac.uk/%7Escd/ShapesOnwards.pdf


Is that really what you get when you put all entity IDs in a set and iterate over those? Sure the component arrays are packed but that's kind of pointless if you're iterating over them in a random order (note that the system proposed here will also gradually destroy the ordering in the packed arrays).

Not that I know an easy way to keep a lists of components that ensure that iterating over them is anywhere close to cache-efficient. Perhaps some kind of self-ordering system would work? (you could for instance move components to consecutive positions as a system uses them, ideally in way that's stable so you don't mess up the ordering of other systems)


If the system starts ordered and is iterated frequently, and things move (due to deletion) rather less than they are iterated, you can use... bubble sort.

It's basically free to do one stripe of bubble sort while iterating, and for games, you'll iterate everything once or more per frame, but introduce and delete things less.


The trick there is to ensure things work nicely if different subsets are iterated one after the other.

Note that merely ordering isn't quite enough, let's say a particular system needs components ABC and component C is rare, then the ideal situation would be to sort A and B such that those entities with component C are close together.


The devil is indeed in the details when one sets out to use memory in this Fortran-esque fashion.



There are some really interesting ECS implementations popping up in Rust, like apecs[0], which supports async systems.

0: https://crates.io/crates/apecs


Something I don't get about ECS: How do you handle interactions between systems, and especially side effects that can affect multiple systems?

Because of those, the systems can't really be considered truly independent, can they?

Doesn't this cause complexity to go through the roof?


Systems communicate via components. They can create empty "marker" components (and remove them again as needed) or, when applicable, put data in shared singleton data structures called "resources."


In some ECS in Rust (like Specs) there is a concept of a “resource” for this. System declare their use of resources, which allows the scheduler to order them.


I like to have a message bus to send messages to queues and have systems consuming those queues.

The shared mutable reference to this message bus would be provided to each system requiring it. It's easily achievable in Rust with Bevy, or with Unity DOTS.

When I don't use Unity DOTS and simply good old MonoBehaviours, I call the message handlers as soon as the message is published instead.


Might be a misread, but this feels a little similar to relational vs columnar databases. Relational databases store a thing's data together; columnar store a type of data together.


A relational DB can store data however it wants, including columnar formats. The interface presented to the programmer (SQL) just abstracts away the details.

ECS are more similar to the relational model than OOP is, and do not necessarily suffer the same 'object–relational impedance mismatch'.


I'm pretty sure a relational DB stores data per-row in a table, whereas columnar databases store data per-column. I don't know how using SQL (or not) affects this.


Bevy[0] (written in Rust) has a fairly mature ECS implementation.

[0] https://bevyengine.org/


It’s good, maybe even the best in the field right now, but it’s not mature.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: