A hybrid Entity-System-Component architecture

Component-Based Architectures

Most people who are interested in engine design and architecture have encountered the concept of ‘Component’-based design, the general principle of which is “favour composition over inheritance”. This is not a game-specific principle, but one of the central tenets of object-oriented design.

If you are not familiar with the idea, consider the ‘traditional’ game hierarchy in which everything might derive from a GameObject class. Directly inheriting from this class, you may have a MoveableObject class and a DrawableObject class. But what if you want a moveable, drawable object? You now need to inherit from either of the two subclasses to make a MoveableDrawableObject. The more aspects of functionality your object gains, the deeper your hierarchy goes, until you have something that looks like GameObject->DrawableGameObject->MoveableDrawableGameObject->Vehicle->VehicleWithAWeapon->Tank. That’s already a pretty deep hierarchy just to describe something as generic as a tank – we haven’t even gone into any kind of specifics yet.

The proposed solution is to compose objects rather than build them using class inheritance. While this brings a certain level of indirection, it also greatly simplifies and flattens the hierarchy. There are also other benefits, such as being able to dynamically add or remove functionality at runtime: you want your menu buttons to bounce around when the game is idle? Simply add a PhysicsComponent to each of them and let the engine do its work. The ‘seminal’ texts on this subject are Evolve Your Hierarchy and the T=Machine series – check these out for a grounding in the basics.

It’s a powerful paradigm, but many people get stuck on implementation. Over the years, I have written and re-written several iterations of an engine based on this approach, in C++, Python and C#. Every time I seem to get a little bit further, but then the nuances of a problem catch me out and it’s back to the drawing board. The chief culprit tends to be ‘how do components interact with each other?’ which is what I hope to address here.

Dependency Hell and other minor inconveniences

The first approach was a fairly simple one, as described in the CP and T=Machine articles: each aspect of functionality is contained within a component, which are then brought together as an Entity (a GameObject). There is no inheritance hierarchy, and the Entity class is simply a list of components with an ID string attached. So, for a simple platformer, we might have:

  • PositionComponent
  • RenderComponent
  • PhysicsComponent
  • InputComponent
  • CollisionComponent

This looks fine, but look a little deeper and you will notice all kinds of dependencies between these components:

  • Collision, Rendering and Physics all rely on the Position component
  • Collision and Physics are related but perhaps separate enough to warrant their own component (perhaps you don’t always want a physical collision response)
  • Input may affect Physics (hitting the Jump button)

The more components you have, the more complex these dependencies become. You could create all sorts of rules like “if Entity has a RenderComponent it must also have a PositionComponent”, but this approach is hardly manageable – the whole point of the component architecture is to keep your code modular, right?

If in doubt, extract it out

After wrestling with these dependencies, it became more and more clear to me that the solution would be to extract the behaviour out of components and into an external manipulator. Apparently I was not the only one who had been thinking this way: after scouring the internet for more resources, I found this thread on a Java-based framework named Artemis that seemed to be doing exactly what I had been. While a little miffed that I had been beaten to it, I at least felt comfortable that my problems were shared and that I had come up with a similar answer.

So now we are using a data-driven approach. The difference is this: Components are now simply data which external Systems control (this is a fairly poor, undescriptive term – but interestingly one that people seem to come up with independently. I have also seem “Attribute/Behaviour” which I think are more descriptive but for the sake of canon I will use “System”).

The RenderComponent becomes a SpriteComponent, which the RenderSystem will use along with a PositionComponent to render the entity to screen. It is still possible to do everything implicitly – an Entity qualifies for rendering if it has these two components available, and the RenderSystem will check every new entity (and listen for component changes). Similarly the PhysicsComponent now merely has physics data (velocity, acceleration, forces) which are manipulated by the PhysicsSystem. Hooray, we have completely separated our concerns!

Do I know you…?

But wait… Isn’t all of this a little familiar? Separation of data and logic feels a bit procedural, no? We seem to have completely eschewed OOP in favour of its predecessor! Well, that is not quite true. Our systems can still benefit from OOP principles – the InputSystem will need to rely on an InputProvider and probably an InputMap and a Command structure etc, etc. It’s just that our game entities themselves will be split up into different systems and components. This is actually another OO principle: the single-responsibility principle. Having one megaclass to represent the rendering, movement, AI decisions, amount of HP, etc. is poor OOP!

The Answer To Life, The Universe And Everything

Now, this is where most people stop – the Entity-System-Component (ESC) architecture is generally implemented as above, where Systems are pure logic and Components are pure data with a flat hierarchy. But I have found a more appealing idea in a hybrid approach. Components themselves may have a (small) inheritance tree and therefore perform some logic.

My favourite example is rendering. In normal ESC, how do you differentiate between rendering a sprite and rendering some text? Would you have a RenderableSpriteComponent and a RenderableTextComponent? Would you need a different system for rendering sprites and text? That sounds slightly ridiculous!

My solution is to have a RenderableBaseComponent, which looks like this:

public abstract class RenderableBaseComponent : IComponent
{
    public abstract void Render(SpriteBatch spriteBatch, Vector2 position);
}

The base class then has two subclasses:

public class RenderableSpriteComponent : RenderableBaseComponent
{
    public Texture2D Sprite { get; set; }

    public override void Render(SpriteBatch spriteBatch, Vector2 position)
    {
        // Render the sprite to the spritebatch at position
    }
}

public class RenderableTextComponent : RenderableBaseComponent
{
    public string Text { get; set; }

    public override void Render(SpriteBatch spriteBatch, Vector2 position)
    {
        // Render the text to the spritebatch at position
    }
}

The RenderSystem simply looks for any entity with a RenderableBaseComponent and a PositionComponent, passing the relevant arguments into the overridden Render() method. In this way we can use all the power of object oriented programming, while still retaining the flexible, modular ESC architecture. Neat, huh?


4 comments

  1. Thanks for referencing my tmachine articles at the top, but … I’m wondering why you didnt follow them. Was there some problem, somethig that didnt work for you?

    The problems you list here were all explicitly solved by my oriinal posts. The posts came out of long experience with using ES in real world projects, and i think if you followed them you might find some of this easier.

    • Hi Adam, thanks for replying, I definitely didn’t expect to see a response from the author of the original articles. The majority of this post is dedicated to the typical Entity-Subsystem-Component structure – I confess that having read your articles a long time ago perhaps I hadn’t read them thoroughly enough (or maybe I did and didn’t quite ‘get it’)! I also hadn’t noticed that there are more recent posts, I’ll be sure to check those out.

      I’m still not sure how your system addresses the final issue? Let’s take the RenderableText and the RenderableSprite as an example – would these components need separate subsystems to perform the rendering?

      • You have methods in your components, which breaks the very first point of my posts :). Once you do that, yeah … all sorts of problems appear, and lots of difficulties arise.

        Get rid of the methods, and I think your problem disappears.

      • I think you are confusing component methods with component dependencies. The goal of a component-based system is to break down the traditional object hierarchy into composable behaviours. If these behaviours (e.g. “Renderable”) lend themselves to a neat OO hierarchy then why not use that? Using a component as a simple bag of data is a waste of the powerful OO paradigm!

        Re: “Get rid of the methods and your problem disappears” – I don’t understand. The problem is solved by component methods.


Leave a comment