The last time we talked about inheritance-based game models, which seem to be the most obvious approach to implementing game objects and logic. We pointed out many of their disadvantages, which essentially boil down to the diamond problem and the fact that all changes in your code propagate along your class hierarchy, making the whole model difficult to understand, maintain and extend.
In this part of the series I’d like to take a closer look at an aggregation-based game model which is popular since Gas Powered Games’ Dungeon Siege, but has been introduced long before. This approach tries to handle the problems of inheritance-based models mentioned before, while providing great extensibility and flexibility at the same time.
Aggregation-based Game Models
“There are probably hundreds of ways you could decompose your systems and come up with a set of classes […], and eventually, all of them are wrong. This isn’t to say that they won’t work, but games are constantly changing, constantly invalidating your carefully planned designs. […] So you hand off your new Game Object System and go work on other things. Then one day your designer says that they want a new type of “alien” asteroid that acts just like a heat seeking missile, except it’s still an asteroid.”
– Scott Bilas
The idea is to implement all game entities as aggregations of components, which in turn encapsulate independent functionality. This corresponds to recommendations by the Gang of Four, who suggest “favoring object composition over class inheritance”. A similar approach is used by the Unity3D game engine, for example.
Let’s assume that we have implemented three components that are to make up our game entities: PositionComponent, HealthComponent and AttackComponent. Most of our entities will have at least the first component attached, for they have to be located somewhere in our game world. Many of them will have a HealthComponent attached as well, such as trees, buildings or soldiers that can be targeted and attacked. The AttackComponent is added to all fighting entities, e. g. knights and watchtowers.
Aggregation-based Game Models – Naive Implementation
The first and most basic implementation of our game model is straight-forward: Just create an Entity class and add references to all available components.
This approach has obvious disadvantages. Many of the component references will be null pointers for most entities, causing a big unnecessary memory overhead. Additionally, the Entity class has to be updated each time a new component is introduced.
Thus, more experienced object-oriented programmers will come up with the idea of introducing a common base class for components soon: Entities will then just hold a collection of Component objects, reducing the memory overhead and increasing extensibility. This modular approach already gets close to an optimal solution, as it is easy to build, maintain and debug, and it’s easy to implement new design ideas without breaking existing code.
However, we can do better.
“All the data goes into the Components. All of it. Think you can take some “really common” data, e. g. the x-/y-/z-coordinates of the in-game object, and put it into the Entity itself? Nope. Don’t go there. As soon as you start migrating data into the Entity, you’ve lost. By definition the only valid place for the data is inside the Component.”
– Adam Martin
Next approach: There is no Entity class at all.
Whoops… How is that supposed to work? Let me explain the architecture of this system first, I promise I’ll explain its advantages (and disadvantages) later.
In entity systems, our game entities are nothing more than just an id. There is no Entity class, and thus no data or methods on entities. Furthermore, there are no methods on components, either. Our components are just containers for data of our game model, all functionality goes into what is called a system. There’s a dedicated system for each part of our game, such as a PhysicsSystem, a HealthSystem or a FightSystem. These game systems are responsible for creating and removing components for all entities, and entirely operate on their corresponding components.
Let’s just assume a new knight with the entity id 337 has been created in our game. This will cause our PhysicsSystem to create a new PositionComponent and somehow map the id 337 to this component. The HealthSystem and the FightSystem will do the same, creating and mapping a new HealthComponent and AttackComponent, respectively. All of these components are properly initialized, setting the knight’s position, health and attack damage.
If our brave warrior gets attacked now, the FightSystem notifies the HealthSystem that the knight with id 337 is to take damage. The HealthSystem will look up the HealthComponent for entity id 337 and reduce the knight’s health, maybe applying armor or other fancy stuff. If the knight’s health has been reduced to zero, the HealthSystem fires an EntityKilled event, notifying all other systems that the entity has died and its components can be removed.
Advantages of Entity Systems
The naive aggregation-based model has one problem I kept quiet about: Adding and updating component in an arbitrary order can lead to subtle issues. You don’t want one entity to update physics before animation, and another one the other way round, for instance. While the naive model requires you to enforce a specific update order on all component collections, this is already implied by the update order of our newly introduced systems.
And there are other upsides as well:
- Memory management. Using systems, components can easily be pooled and re-used whenever an entity has been removed, saving much time that would have been spent on memory allocation otherwise.
- Multi-threading. Independent systems can be updated by separate threads. This can – and will – give a significant performance boost, as modern game hardware tends to provide more and more CPU cores nowadays.
- Serialization. As there’s no entity class and all data is held by the components, this data can easily be serialized and stored in a database. That’s the reason why many modern online games are based on entity systems, using one table for each component type.
Disadvantages of Entity Systems
The most obvious downside to entity systems is the performance hit that is caused by all the look-ups. Resist the urge to add cross-component references, as this will make you lose all of the advantages mentioned above. If you think about making your HealthComponent reference a VisualEffectComponent for displaying a nice particle effect every time our fellow knight is hit, remember that there could be several VisualEffectComponents attached to the knight… In most cases, just firing an event and making the event system do its job is still the best idea.
In order to avoid running into performance issues, just don’t flood your system with unnecessary component types: Think carefully about introducing a new component, just as you would when introducing a new subclass in inheritance-based models.
One might argue that it takes longer to “get the job done” with such a system, as it looks like you’ve to think much about your architecture before you’re able to see any results. I disagree: Our team has used an aggregation-based game model at the InnoGames Game Jam a few weeks ago, and pumped out a multi-platform multi-player real-time tactics game in just 48 hours. Spending the little extra effort at the beginning pays off. Always.
Knowing that inheritance-based game models show a lot of disadvantages, we have taken a look at aggregation-based models and their up- and downsides. Entity systems are easy to maintain and debug, and they provide great extensibility without the necessity of modifying existing code all the time. They show better performance characteristics for both memory and CPU load, and allow you to add scripting and serialization with a minimum of additional effort.
Whenever you’re working with this kind of system, feel free to pass by and share your feedback in the comments, I’m looking forward to hearing from you!
- Mick West. Evolve Your Hierarchy. http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/, January 2007.
- Levi Baker. Entity Systems Part 1: Entity and EntityManager. http://blog.chronoclast.com/2010/12/entity-systems-part-1-entity-and.html, December 2010.
- Kyle Wilson. Game Object Structure: Inheritance vs. Aggregation. http://gamearchitect.net/Articles/GameObjects1.html, July 2002.
- Adam Martin. Entity Systems are the future of MMOG development – Part 1. http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/, September 2007.
- Adam Martin. Entity Systems: what makes good Components? good Entities? http://t-machine.org/index.php/2012/03/16/entity-systems-what-makes-good-components-good-entities/, March 2012.
- Scott Bilas. A Data-Driven Game Object System. http://scottbilas.com/files/2002/gdc_san_jose/game_objects_slides_with_notes.pdf, Slides, GDC 2002.
- Scott Bilas. A Data-Driven Game Object System. http://scottbilas.com/files/2002/gdc_san_jose/game_objects_paper.pdf, Paper, GDC 2002.
- Insomniac Games. A Dynamic Component Architecture for High Performance Gameplay. http://www.insomniacgames.com/a-dynamic-component-architecture-for-high-performance-gameplay/, June 2010.