ECS pattern in gamedev
Gameplay logic is considered one of the most challenging topics in gamedev. If you attempt to build gameplay relying solely on organizing classes within an OOP paradigm and cascading if-else conditions, you can quickly lose your bearings. The code becomes so tangled and difficult to understand that even the developer themselves struggles to decipher what’s happening. The increasing complexity of these conditions makes adding new features costly and labor-intensive.
We encountered this problem when implementing gameplay for LandLords. Our initial code for game scenarios was exactly like that — a certain order of execution, but also heavily saturated with conditions, making it incredibly fragile and inflexible.
I’ll explain how we refactored the game scenarios using ECS. Just to note, the game engine for LandLords is Phaser, which only creates prefabs; there's no way to programming gameplay. Using ECS, we significantly simplified development, resulting in more understandable, maintainable, and extensible code.
ECS (Entity Component System) is an architectural pattern that allows you to separate data, behavior, and control logic into distinct parts, making the code more modular and flexible. ECS enables you to organize your code so that complex relationships become transparent and easily manageable.
First attempt at writing gameplay
We already had a playable game — most of the game scenarios were already working somehow. But as a developer I felt an undeniable discomfort working with the gameplay code; it was inextricably linked to constantly evolving requirements and game situations. I frequently got lost in the order of execution and cascading if statements. Often fixing one place would break another. And each time it became increasingly unpleasant to think about adding something new to the game.
Of course, there was control over the logic, but it was weak. It lacked the flexibility needed to dissect game situations with pinpoint accuracy. I couldn’t reconcile myself with this — a refactoring was urgently needed. I started investigating the problem and searching for a solution. After reviewing several presentations from other developers facing similar issues, it became clear that ECS was precisely what was needed to address these complexities. Although I still didn't fully understand how it worked.
ECS concept
ECS offers a different approach to game development than we're accustomed to, relying on OOP. For games, you no longer need to create hierarchies of classes and configure relationships between them. Instead, game data is stored in global registry-arrays, and data management is handled within individual system-functions that execute in strict order. This effectively separates data from logic:
- Entity — is simply a numerical identifier that ties together components.
- Component — is the data attached to an Entity, such as position, velocity, or health.
- System — is the logic that processes the components of entities.
ECS seems like a more organized approach, but its essence as a design pattern is deeper. It's a fundamental mind-shift in how we think about game development. Instead of thinking about how objects interact with each other, we start to think about what they do and what events are occurring. And this approach allows developers to create much more flexible and adaptive code. You can model anything — it’s not necessary to think about objects while doing so.
Specifically for LandLords using ECS I modeled user-defined events and the composition of game situation indicators. I created a small demo project in which I demonstrated this with a minimal example. The demo shows click handling and fizz-buzz string generation based on a counter. This is what happens in the game, but on a much larger scale.
github/13luck/ecs-pattern-demo
Refactoring
After a thorough search for an ECS template library, I chose bitECS. At the time of writing this article the library is in version 0.3, but it already provides all the necessary functionality. I appreciated its minimalist API, which allows you to quickly and easily integrate ECS into your project. It’s precisely that desired API with pinpoint accuracy.
For events, I use an entity with an attached marker component. And for game situation indicators, I use an entity with a composition of marker components. With this approach we were able to achieve maximum determinism in gameplay. This gave me, as a developer, complete freedom to flexibly adapt to the changing game requirements and simplified further code development.
I managed to rewrite the existing scenarios in just over a month and a half. Players didn't notice any difference, but the game scene code became significantly more structured, with a clear separation of responsibilities between systems. Although there were some ‘rough edges’ along the way. Furthermore, unit testing became easier, and thanks to code optimization, it was faster. Memory consumption also decreased.
Ultimately, ECS resolved specific problems within the project, such as the complexity of hierarchies and performance issues. It all sounds like we've moved from chaos to order. ECS is truly a structured architecture that makes gameplay tasks predictable and, in theory, perfectly suited for AI automation. The code looks so template-like that it seems like an AI could take on some of the work if given clear instructions.
One of the drawbacks of using ECS is the appearance of boilerplate code — this doesn’t cause significant discomfort, but can slow down development. ECS often requires frequent use of for-iteration to iterate over entities — this is quite typical for ECS solutions. There are likely additional abstractions or tools that help minimize this drawback.
Conclusion
There are many different approaches — some experimental, some alternative, but there are those that work definitively. And the ECS approach falls into the latter category, as I've personally experienced. If you’re only working on a part of a system, say on a large project with a big team, you might not immediately appreciate how justified this approach is. However, if your tasks include architecture or you’re leading a personal project, the effectiveness of ECS will become obvious to you. Typically, when we undertake a game development project, we anticipate its evolution — which includes growth in the team, changes within the product, and other factors; that's why ECS offers the possibility of scaling without the curse of chaos and disarray. I now can’t imagine how gameplay could be written any other way — ECS has become a natural and effective way for me to organize code.