Please don't put ECS into your game engine

As a game developer with over 25 years of experience, let me assure you that Entity Component Systems exist and are popular for good reasons. Performance is one key benefit, but also an ECS achieves a kind of data normalization similar to SQL databases. This relational model affords flexibility that is difficult to maintain in OO systems.

I'd take a Rust ECS over a Love2D engine any day. With the freedom and speed of unopinionated Lua development, most developers (self included) will quickly paint themselves into a corner. Change will become difficult because the engine does exactly what you tell it to do, and you will contradict yourself as requirements and implementations change. The ECS will, of course, do what you tell it to do also -- but it will do so in a manner that is structurally consistent. Your way of telling it what to do is far more organized than evolved imperative code. This gives you greater control, makes change easier and more predictable.

Don't worry about Conway's Game of Life. It doesn't discourage ECS any more than it discourages a class hierarchy. Think clearly about what the evolution of state actually involves: a previous state, an action, and a new state. If you've seen implementations that make use of already-updated state as if it were previous-state, then those were simply naïve implementations. You can easily achieve correct updates by flip-flopping between two full states, but in some cases like Life where change depends only on local previous state, you can fill and use a small cache as you go along, using only a margin more than one full state.


As I have said

The issue I am talking about is just that

And my proposal does not actually goes against ECS in any way (or at least I don't see how it could)

There are just too many advantages not to choose a ECS to be one of the main components of a fully fladged new game engine aimed at building (up to) complex and very features rich games. And these advantages, as @voidshine and others already mentioned, go from performance to the fact that a data oriented architecture also helps with how games are usually structured, as well as keeping your dependency graph simpler.

On the other hand, I also think that the vast majority of games out there (which are not AAA) do not exactly need the complexity that comes with ECS (be aware that other complexities exists without), and they do not need the performance that is achievable with ECS.

There are alternatives, there are other simpler engines that have analogies but may be restricted to smaller problem set (shameless plug, but in the end ECS is just another tool that may fit very well some architecture or just be overkill.

Yeah ECS is great but when you have to make an entity to hold global game state (like score, etc) -- that, to me, seems awfully repulsive. As if people building engines refuse to acknowledge existence of things other than primary game loop. All the Rust game engines that currently exist (that I am aware of, that aren't piston, that aren't a tiny project) basically unsuited to build anything except single game loop.

Scale and performance?
Sure, they provide, they deliver.

Nested game loops?
You're on your own with fragile stateful scaffolding of ad-hoc entities.

What's a nested game loop

1 Like

It, due to state of affairs, is a purely semantical thing (although there's coroutines in Unity, that do something akin to that).

Allow me to introduce an example.

Competitive game of Counter Strike could be described as multiple nested game loops.

  • Wait for players to connect for 5 minutes + 1 minute warmup
    • Action
  • Rounds repeat until one team wins 16 rounds or total of 30 rounds is played
    • Action
  • Final Screen

So that we could, hypothetically, model the game as a nested series of loops

while (session) {
    while (wait_or_warmup) {
        action(); // with all the actual ECS business
    while(repeat_rounds) {
        while (round) {
            action(); // with all the actual ECS business

Or, as a state machine, which would be more suited for Rust (because Rust does not have coroutines).

Ah I see, in my opinion that kind of thing would best solved outside of the ECS. When I saw Amethyst's state stack architecture I thought it looked like a nice way of handling it :slight_smile:

(not that this conversation has anything to do with amethyst, just that amethyst was where I first saw the idea)

I'm glad you brought up Amethyst. I was looking at it myself just today.

That would be State Manager, right? That's a good example of how an engine could have something other than ECS at the base of it's state management.

Although I would prefer if it let me fill that space with my own logic, that is still at least a single a step in the right direction.

What is stopping you? Amethyst's state stack is like a page of trivial code, and you could probably replace it with whatever you like in an evening.

Have you ever written a game using an ECS? Judging by the things you're saying about it in this thread, you're punching at ghosts. They don't have the problems you say they have, and they're not even used in the way you seem to be assuming they're being used. ECSs hold entities and process them. That's all they do. They don't hold all game state. They're just a database of game objects. An ECS isn't a game engine, it is just a small part of one. Any part of your game that you don't want in an ECS can be placed outside of the ECS. An ECS does not constrain the design of your game in any fashion.

You may as well be arguing that webservers shouldn't use RDBMs just because they're not conducive to handling user input and displaying content. An ECS is not a game engine. It's not even an architecture for a game engine. It's just a way of pooling and accessing resources.


Huge pile of dependent code with all the borrowed references and trait bounds (and most of those traits are private).

I am not talking about ECS, I am talking about game engines.

I am not assuming, this is actually the way they are being used, I've seen it many times.

Even the Amethyst's pong tutorial stores scores on the actual scoreboard entity. Because there is no better option, it seems, because they made ECS, and made it mandatory.

ECS does not, the game engine could, however. The best option I see, with, say, Bevy or Amethyst, is using global lazy-static variable. Could be better.

It's a TUTORIAL. It's not an example of how to design a game, it's an example of how to use the APIs. You are not required to do what it does. There are better options, but pointing them out in a tutorial is not conducive to making a concise and informative tutorial.

The basis of a good argument is establishing credibility and presenting compelling arguments. All you're presenting here is "I personally can't do it right" and "trust me." You can do better than that. For starters, show the code you've written. Link to the code that you're seeing problems in. Show me how your conclusions are necessitated by a specific design. (I personally have made games in Amethyst, and I know for a fact that you do not need to put scores in the ECS as entities. There's nothing you can say to convince me otherwise. Do you see why this is? What is your problem with resources?)

Because my experiences are completely contradictory to yours, and there's no point in playing "he said she said" by comparing opinions about unobserved data.

1 Like

[Moderator note: Please tone it down a bit, folks.]


The resources are nice and I like that they're there. It even states it in the Amethyst book, even about "score of a pong" specifically (sorry, I missed that).

The issue for me is, though, that it's hard to make hierarchical structures out of just flat sets of components and systems (take, for instance, my example of nested game loops). That whole deal is, essentially, procedural programming with a lot of global variables, except order in which procedures are called is not guaranteed. As a result, as it seems to me, a fragile stateful system appears, with a lot of, as Rich Hickey put it, "I will put the acorn behind this tree, and you come by later and find it behind the tree" logc.

Though it's not a game engine, I've been working on the cala crate which can be used to make 2D and 3D video games, as well as GUI applications. It's not complete by any means, since graphics currently only work on Linux (other features are cross-platform). Maybe it'd work for you? If you do try it, I'd like feedback. There's no ECS involved in it, but rather shared and private contexts for asynchronous tasks (similar to the design of the Tide webserver). I eventually plan to build a game engine depending it (which will mostly be physics functions).

This is almost certainly not intended to be implemented with multiple isolated event loops. And if it was actually done this way in CS, they probably had a bad reason, like cutting corners to meet some arbitrary deadline? These states are actually handled by a ...

You said it yourself. You're not confusing ECS for FSM, but I still can't figure out why you're do hung up on a crusade against ECS? The irony that ECS manages game state and state machines manage state transitions is not lost on me, but these are also very distinct responsibilities, so there should be little confusion with that as well.

This is that confusion showing up. Nothing intrinsically prevents you from structuring your game state with a hierarchy. The ECS may query a flat list of entities, but this can also be seen as a flat list of root nodes. If you want to make state transitions like main menu -> play a match -> results screen, you don't do that in ECS.


Well, as I said

Although it might not seem like it, I actually like ECS. I am arguing specifically against ECS being at the bottom of everything.

Yes, but the engine has to allow some freedom to do that. Amethyst's state transitions (the State trait in general) is the best option that I've seen, though it is a little restrictive, given that only one type of data could be shared between states, and, I expect, could lead to the acorn-behind-the-tree ad-hocs.

I am currently looking deeper into what Amethyst has on offer, it seems to reflect exactly what I'm talking about.

Coroutines are a completely separate topic, which involves concurrency. State transitions are done in Unity (or UE, or Godot, or pick your favorite engine) with scenes. You can think of a scene as a container for a scene hierarchy.

It's a hierarchy because that's how humans typically think of the structure that makes up the game world. And that makes editing quite intuitive. That hierarchy just gets flattened when entities are added to ECS at runtime.

A crude example of such a transition is just clearing the ECS world and populating it with all entities from the next scene. What's interesting about this approach is that it is purely data-oriented. There is no explicit "state machine" beyond the hypothetical state machine that is created by various entities and systems loading each new scene. In other words, you don't write nested loops like you've shown. You just have a main loop that drives the state machine through systems.

Sharing state between scene transitions, I don't see why that can't just be another component perhaps using continuation-passing style. Something like the load_scene function accepts an argument for state to carry over.

I thought the whole motivation behind ECS is that there are no nested game loops. There is only one. It is unavoidable. It is dictated by the physical reality of the frame refresh rate. At 60fps you have 16.6ms to figure out what it is you want to display and get it rendered.

Given that hard constraint and the desire to have hundreds/thousands of objects in a scene it turns out representing those objects in typical OOP style does not meet the requirement. Typical OOP style splatters your data all over memory in a cache unfriendly and slow way.

Enter ECS. It organizes the data of your "objects" into nice neat arrays in memory that your processor can rip through applying updates and transforms very quickly. Avoiding the huge penalty of cache misses.

What am I missing here?

I am no game writer but this is all reminiscent of real-time control systems. Think aircraft control systems. Where one typically has a hard deadline on performing the critical work. 10ms or whatever. If there is other less time sensitive work to it gets done a bit at a time over many iterations of that time tick. Those jobs may well have state machines of their own.

Although I've called them coroutines they are actually a generators (they're called coroutines in some places for some reason), that are invoked every frame. They are going to be added to Rust at some point it seems.

That feels like constructing a Rube Goldberg machine.

Do you mean Heath Robinson? :slight_smile:

Perhaps so, but the solution is driven by the problem. If your game does not demand a 60fps update and rendering of thousands of objects perhaps you don't need ECS.