Moving to an Entity Component System (ECS)

While working on redoing the game logic using a custom physics engine and just delegating rendering to Godot, I’ve started to re-evaluate using a full Entity Component System (ECS) approach. I just spent a couple of days basically just commenting out code and putting an absolute ton of not implemented exceptions into the code. The most egregious place being the Microbe class, which even when split into multiple files is quite a behemoth. I had originally said that redoing the game with a full ECS approach would be a lot of extra work. Currently we kind of have code in that direction with various quite independent “systems” (the S in ECS). So we already have a bit of a hybrid design where fundamentally many designs are obviously modelled after the ECS model, but our entities aren’t really done like that. So my opinion has changed from what I last said on this.

So as a quick summary, I think it would be good to go full ECS for Thrive:

  • We already have a kind of a hybrid thing and with at least the Microbe class being a lot of dense code, I think it would actually clarify the game architecture
  • ECS approach is said to be a lot faster with many entities thanks to cache locality and vastly easier multithreading (I was thinking how I’d do that while refactoring the game logic, and I came to the conclusion that I wouldn’t be able to do this well without basically copying an ECS approach)
  • Turns out that the logic rework is already difficult, but also boring to me at the same time as this is like the 4th time I’m working on the base microbe logic (thanks to all the engine changes etc.)

So a solution to all of those would be to go all in on an ECS and rebuild the game entities from the ground up using the ECS paradigm and making all of the game systems with smaller parts. Especially making the Microbe class work again after the conversion to the custom physics, seems to be a pretty huge and complicated monolith of a task, which would be easier to do piecemeal with ECS components.

I found 3 potential ECS frameworks to use:

I picked those potential candidates based on a benchmark where those 3 were the fastest ones:

Those Arch and HypEcs are basically the fastest ones (their order depends on the test), with DefaultEcs still being pretty fast, but having an absolute ton more features. Arch is the most basic one but it allows basically direct access to the data for custom threading etc. to be done so it might be the fastest possible one (HypEcs has a threading implementation drawback when there aren’t a lot of distinct entity kinds).

Even though basic Arch might be the way to start as it has the bare minimum features, so converting from it to any other ECS framework should be pretty easy as it only really has the most basic of features. Also it is the fastest in many tests so it would serve well our goal of improving game performance.

If I go ahead with this, I’ll leave the prototypes as is, but I’ll be able to ditch quite a lot of duplicate code that I needed to already add to allow custom simulated and Godot entities. The ECS approach would thus allow me to ditch some unnecessary boilerplate and start reimplementing the microbe logic in a lot cleaner way. I already saw a bunch of bugs / workarounds in the Microbe class that needs to be removed even with a slight logic rewrite so I really think a full rework would be faster to do, partly because I have more motivation to work on that.

Thoughts on my plan on redoing the game as ECS properly?


Here’s some extra links on ECS performance and what’s the philosophy like:

1 Like

I’m so far not the best-placed one to answer to this but I’ve tried to understand it overall.
ECS sounds like a double-edged solution where it seems to really work faster and easier for programmers, but in another way have quite a tiny amount of documentation.
So I might tell you to go for it only if you really think you can handle it without counting much on external help o.o’
Another “negative” point would be that it will (at first) slow down again the completion of the microbe stage that is being developed for ages.

Again, I’m probably not the one that is really concerned about that change. The only reference I have with programming is the visual blueprint system from Unreal engine x)

From the way you’re describing this, it sounds like this is a win-win situation where the refactor would go faster and have better results this way. Given that performance is such a big pain point to the players, I’d say it’s worth the investment.

As with the previous refactor, I’d want to ask what areas are NOT being impacted, so that other programmers would know what kinds of changes aren’t going to turn into a mess of conflicts.

2 Likes

GUI stuff is mostly safe, there’s only a few tiny changes needed to the C# side of the microbe HUD, but all other GUI stuff should be untouched. Also most graphics are untouched, there’s just a few entity related scenes I need to rework. A bunch of the general and species etc. code will be also untouched.

The most conflict prone stuff will be gameplay Node and microbe stage related code. Especially the Microbe class and to a lesser extend the other entities and the microbe stage gameplay systems will be pretty nightmarish to fix conflicts on. I don’t think that many conflicts will arise as it’s been pretty quiet development-wise the past month. So if that continues even after my vacation (I’m very unlikely to be able to finish this before that), there could be as low as 0 conflicts.

That’s just that one library. And that one library was so small that it only takes 15 minutes to read all of the code in there to figure out what it does. Anyway it seems that Arch has at least pretty good documentation for how to use it, so it should all be fine if I pick that.

It’s of course impossible to say and fully evaluate your mental energy, but I’d guestimate that doing this ECS conversion will give me enough of an increased enthusiasm towards this whole thing that overall the game logic rework will also finish faster. So I’d estimate this ECS conversion to actually have a negative time cost. Programming is funny that way that a single fully motivated workday with good flow can result in more finished work than a week of not very motivated pace.

Well judging by this you seems to love programming as much as I love making 3D x)
I happened to work around 75 hours in the same week on the same project. This rewarded me to be the most downloaded Space Engineers’s mod for 2 months !

So yeah, okay, even when we point negative side you’re like “I don’t care I wanna do it anyway”.
I fully agree with you that one motivated guy can work waaay faster than anyone (even multiple people) that is not!

Go for it!

1 Like

I sat down and tried to understand what an ECS is and what it entails. Now fully understanding it, I agree that this could be a large advantage. It’s a decision that seems to make a lot of sense for a game like Thrive with tons of different entities.

Question: Do you plan to treat each species of microbe as an individual entity type with its own components, or do you plan to use it in a different way?

I think the usual model is that you can have only one instance of a component per entity (and that seems to be the case for all the libraries I looked at). So for example if I decide to model the organelles as entity components (which I’m not yet fully sure I should as they are quite internal to the microbe), there’d need to be some bundling like all flagella are bundled into one component. So the end result is that each species is not going to have that different entities. I think about the only difference between the microbe entities will be that the AI microbes will have an AI component to store the AI memory, and the player obviously doesn’t need that.

One design I’m not fully sure yet on how it would best make sense to create the visual nodes and physics, which are external to the ECS world. One potential way I’m thinking right now is that the components will have some ephemeral data that is generated by some systems when microbes are missing that stuff. For example the Godot nodes and then they will be attached by a different system that also takes care of QueueFree’ing unused things. This allows very easily saving the world state to JSON and re-loading the component instances afterwards. And the system then after load will setup all the external data automatically without any special save load handling. I don’t suppose anyone else knows a different approach for this?

Turns out that out of the 3 libraries I posted earlier, only one of them is compatible with Godot 3. The other 2 require much newer .NET runtime than mono can provide. I don’t think I’m in any shape or form ready to consider also converting things over to Godot 4 at the same time…