News for hackers. Stuff that matters.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by TeMPOraL on Monday December 11, 2023 @04:00AM (#38598848)
Ah, ECS. The most ill-defined software pattern since IoC/DI. Everyone seems to have their own understanding of it, usually somewhere on the spectrum between "what if we kept the game state in a relational database"[0] and "what if we put any individual type of information into its own big array, so it's CPU-cache-friendly"[1]. I wrote several such systems for my own toy games, at various points of that spectrum[2], and I still have a bunch of open questions I can't seem to get my head around.

The biggest such question is: how the hell do you handle "cross-cutting concerns" in an ECS architecture, especially in the data-oriented programming version, where "cross-cutting concerns" are basically any kind of logic ("system") that needs to access more than one component of an entity at the time, especially if it does so conditionally? Like e.g.:

- Physics, rendering, animation, and game logic all need to access some subset of the same position, orientation, dimensions, velocity, acceleration, mass, tensor of inertia, etc.

- Any kind of logic that goes like "IF something(component 1) THEN doSomethingTo(component 2) ELSE doSomethingTo(component 3)".

How are you supposed to preserve data locality in such cases? Is it even theoretically possible?

I may have some fundamental misunderstanding about the definitions here[3], but I haven't found any clear answer to the questions above, whether theoretical or practical. Back when I last looked, couple years ago, I couldn't find any non-toy game written ECS-first and with source available to study. Maybe this has changed now.

--

[0] - Which, as far I recall, was the original idea behind the pattern. It's also a very interesting one in general - if you squint, a lot of code all of us write for our projects is just half-baked attempts at setting up specific indexes and hand-rolling queries to a bunch of vectors, hashmaps, or (gasp) object graphs.

[1] - AKA "data-oriented programming", in this case mostly preferring "Structs of Arrays" over "Arrays of Structs".

[2] - One of them was literally just "let's store all game state in an in-memory SQLite database, because guess what, it's actually fast enough to be queried at 60 FPS!".

[3] - In my defense, most of the guides, tutorials and articles I read back in the day were themselves confused between "SQL approach" and "SoA approach" (see [0]), or worse, mixed in "whatever abomination Unity passed as ECS back then" and even something semi-related from .NET world. It took me a lot of time to understand that everyone's using their own blend of all those ideas, and this left me unsure about what one's really supposed to do.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by Joking_Phantom on Monday December 11, 2023 @05:10AM (#38599188)
I think the answer is that ECS isn't supposed to solve those problems directly. It's just a framework that makes a certain class of problems very easy to solve. But big picture complexity is still up to the developer's skill and wisdom. Knowing which parts of the game should go into ECS, and which parts go somewhere else. Which systems flow from another system. Which system is allowed to manipulate the data directly and which systems are only able to read data reasonably, but not write. And how your design of the game may need to change to suit the limitations of your computer and your ability to program.

ECS is like any other framework. It is a tool or system, for organizing your efforts. Be very liberal with using it in its intended scope. Be judicious when its at the edge of its scope. Be very skeptical when its outside of its scope.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by TeMPOraL on Monday December 11, 2023 @08:05AM (#38600292)
You seem to be coming from somewhere closer to "like SQL" than the data-oriented end of the spectrum.

On either end of the spectrum, there's much less flexibility. The "SQL side" is optimizing things for bulk operations[0] and flexibility coming from composition[1]. The "data-oriented side" is optimizing for performance, and it so happens that stuffing data that's processed together into arrays you can just scan in a cache-friendly way, also yields a component-like division of data.

Both those approaches are quite inflexible. They do kind of meet in the middle, as they yield similar data organization, but I'm increasingly convinced this is a surface-level, entirely incidental similarity. Philosophically, the two extremes of "ECS" are entirely unlike.

--

[0] - Again, AFAIR, ECS originally came from MMO world, where they do use relational databases for storing game state.

[1] - Also reason to use databases if you're making an MMO, as relational tables are known quantity, while serializing polymorphic object graphs is plain annoying.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by rcxdude on Monday December 11, 2023 @11:13AM (#38601848)
The performance side is a bit overblown, in that of course performance strongly depends on access patterns and the access patterns strongly depend on what you're actually simulating. ECS can help with performance but it can also hurt: e.g. the devs of factorio, who have a very large simulation, based on their profiling, have found the game is almost entirely memory bandwidth limited, and so an ECS SoA system like is often touted would not work very well, as it's far better to run every system on each entity than it is to feed each entity through each system, and when inevitably different systems care about the same components, the first will almost certainly find the component data in the cache, while the latter almost certainly won't be and results in the same data being loaded from RAM multiple times.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by katzenversteher on Monday December 11, 2023 @06:30AM (#38599666)
I believe Space Station 14 is implemented using an ECS engine. It's open source but quite impressive regarding scale and features. It's also a multiplayer game which brings some extra questions to the usual ECS ambiguities.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by ajuc on Monday December 11, 2023 @06:49AM (#38599774)
> Any kind of logic that goes like "IF something(component 1) THEN doSomethingTo(component 2) ELSE doSomethingTo(component 3)"

What's the problem here? You write a system that queries these 3 components and then call regular functions. In bevy (a rust ECS framework) it would look sth like this:

    fn complex_system(
      query: Query<(&Component1, &Component2, &Component3)>,
     ) {
      for (c1, c2, c3) in query.iter() {
         if condition(c1) {
           doSomethingTo(c2);
         } else {
           doSomethingTo(c3);
         }
      }
     }
I think there's no need to deconstruct everything into smallest possible systems, this level of granularity is ok. You could also make the condition into something that can be selected by the query and remove the if.

    fn complex_system2(
      query: Query<&Component2, With<ConditionFullfiled>>,
     ) {
      for c2 in query.iter() {
           doSomethingTo(c2);
      }
     }
     fn complex_system3(
      query: Query<&Component3, Without<ConditionFullfiled>>,
     ) {
      for c3 in query.iter() {
           doSomethingTo(c3);
      }
     }
But that's only possible if you can make the condition be simply existence of some component in given entity. Could be improved with systems that query based on values of components and indexing could be added, of course, but I haven't seen that kind of ECS yet).
Re:The Invisible Bunnies That Power World of Warcraft (2017) by TeMPOraL on Monday December 11, 2023 @07:30AM (#38600024)
You seem to be coming from the "it's more like SQL" end of the spectrum.

Yes, in that design, my questions aren't hard - but then, this design doesn't give you all the touted performance benefits, since in a data-oriented ECS, you're supposed to iterate over arrays of values directly (Rust may be doing some magic here I don't understand, though).

> Could be improved with systems that query based on values of components and indexing could be added, of course, but I haven't seen that kind of ECS yet).

I tried to implement exactly that the other day, including with conditions on values; my overall approach to that was that each Query/Condition had its own array of entities, and all the Query/Condition array of entities were updated on operations like adding/removing components, so the checks are done only when their outcome could changed - which is less frequent than "for every entity, every frame".

It is at that point I realized I'm just reinventing database indices and materialized views, and papering them over with Lisp macros to remove boilerplate - which led me to ditch that ECS implementation, and go for "let's just move all game state data to in-memory SQLite database, and see how it works".

Re:The Invisible Bunnies That Power World of Warcraft (2017) by ajuc on Monday December 11, 2023 @06:19PM (#38606843)
Even before hearing about ECS I wanted some kind of in-game in-memory database for game objects (preferably with history) so I could write quests and dialogs conditions in general way.

Like "If player seen this kind of a monster already and there's at least 4 people in the room and someone there has this kind of weapon equipped - enter this branch in dialog and progress the quest".

Traditional solution seems to be hardcoded special cases for all conditions which probably has better performance but sucks so much when you're implementing quests and dialogs. You tend to avoid writing quests that require new kinds of data so you end up with fedex quests and murder quests and that's it :/.

How well did this SQLite idea worked out for you?

Re:The Invisible Bunnies That Power World of Warcraft (2017) by octavius__ on Monday December 11, 2023 @06:54AM (#38599796)
> Physics, rendering, animation, and game logic all need to access some subset of the same position, orientation, dimensions, velocity, acceleration, mass, tensor of inertia, etc.

I wish I knew the solution to this exact problem. IMO this is the central issue that stops pure ECS from being useful.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by TeMPOraL on Monday December 11, 2023 @07:32AM (#38600047)
You seem to be coming from the data-oriented end of the spectrum.

I spell that out to highlight the part about ECS being a very ill-defined programming pattern - three parallel replies, the different points on the spectrum :).

Beyond that, thanks - I'm relieved to know I'm not the only one with this problem.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by bob1029 on Monday December 11, 2023 @07:20AM (#38599957)
Thank you for this. I've been waffling on just using SQLite to store all the game state. I will give it a shot. I know it will break down for larger scenes, but I don't think I have those yet. Simplicity is probably way more important than anything else for a solo dev custom engine experience.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by Joker_vD on Monday December 11, 2023 @09:25AM (#38600889)
As for your DB remarks, I've recently read an interesting blog post [1] about using SQLite as a cache in the context of web frontend. Apparently it works Well Enough™.

[1] https://sqlsync.dev/posts/stop-building-databases/

Re:The Invisible Bunnies That Power World of Warcraft (2017) by deadbabe on Monday December 11, 2023 @09:40AM (#38601027)
You seem to have deep fundamental misunderstandings about ECS systems.

You can absolutely access multiple components in a system, for some reason you think a component and a system must have a one to one relationship, when really it’s many to many.

The only caveat is that you have to think carefully about the order that some of your systems are executing in the game loop, since you want to make sure component data has been updated appropriately for later systems to act on.

ECS is really the best way to make games. It lends itself well to rapid iteration and experimentation of new game concepts.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by Jyaif on Monday December 11, 2023 @11:11AM (#38601825)
> ECS is really the best way to make games.

A little bit of nuance would go a long way.

Even in games where ECS is a good match, having to implement a boss with ECS is wasteful in terms of dev time and performance: You have to create components and matching systems unique to the boss, where with a traditional approach you just have to create a single Boss class.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by Coder1996 on Monday December 11, 2023 @12:03PM (#38602302)
I suppose it depends on how the ECS is coded.

You could create a database table called "Boss," that contains all the Boss components. Then for each system, you create a function that operates on a Boss.

Re:The Invisible Bunnies That Power World of Warcraft (2017) by deadbabe on Monday December 11, 2023 @01:42PM (#38603636)
A boss is nothing special it is just an entity, also after you beat up the boss you can have later enemies use components from the boss to reflect the players progress in killing enemies using similar mechanics as the boss, until you reach a final big boss.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by TeMPOraL on Monday December 11, 2023 @03:10PM (#38604863)
On the other hand, the classical approach lets you treat the boss as the one-off actor of an one-off scene, which it really is. This narrows the scope of your work (and associated testing). Integrating it into your overall ECS means you either make all aspects of a boss fully generic, working everywhere, or risk someone at some point adding "SpecialLaserCannonHelmet_Boss1" component to a random mook and breaking the game.
Re:The Invisible Bunnies That Power World of Warcraft (2017) by deadbabe on Monday December 11, 2023 @04:22PM (#38605666)
It wouldn’t break the game, it just probably wouldn’t render correctly since a boss works with certain graphics.

Having a one off actor is really limiting. You can create several boss components and build entire permutations of bosses just by mixing and matching them up, without writing specialized code. Designers can put together the entities without developer input.