Now you’re thinking with components

in Design Principles

In an average game there will be a lot of different types of game objects. Since these game objects often share behaviour, it makes sense to categorise them to avoid code duplication. The common solution — in object-oriented languages, that is — is to use inheritance. Inheritance does have limitations however and on further inspection it does not appear to be the obvious choice at all. Switching to a component-based approach resolves a lot of issues. Developers using Unity will probably recognise the alternative I will discuss in this post, and I would lie if I was not heavily inspired by it. My main focus however will be to explain why a component-based approach is used. By explaining the philosophy behind components I am hoping to give existing Unity (or another game engine) users a better picture of the intended use, and new users an interesting new take on game programming.

Before I go any further, I would like to refer to this GitHub repository on which I implemented a minimal example of the code I will also be discussing today. Feel free to comment and contribute, and even use it for your own game if you so desire!

The problem

Inheritance has many limitations. This has sprouted a discussion that is so big, it even has its own Wikipedia page: composition over inheritance. I will not go into detail about the specifics of this discussion, as there are numerous sources on the internet who can probably cover it better than me. So instead, I will give an example which shows how inheritance just doesn’t do when dealing with a large variety of game objects:

Let’s consider a very simple game in which we have a player, enemies, and bullets. In the game we have physics which causes the objects to intersect and fall down because of gravity (but not bullets of course). Let’s try to model this with inheritance.

The player and the enemies both can move around, so let’s assume they share a lot of code. In fact, let’s make them both extend a shared Unit class. The Unit class can in our current model also implement the gravity, because both of them have that behaviour. Finally, everything collides, so let’s have both the Unit class and the Bullet class inherit from a Solid class.

classdiagram1

So far so good. Now your game designer comes along and wants to add a new type of enemies: flying enemies. Alright, so flying enemies still have to be units, but they can’t have the gravity physics units currently have. In fact, the movement code for flying enemies is probably entirely different from the code for normal enemies.

As it stands, there is no good way of creating a strict categorisation without code duplication. The most elegant solution would probably be to strip the Unit class to its bare minimum, keep Player as it is, and separate WalkingEnemy and FlyingEnemy as children of Enemy.

classdiagram2

Of course, even smarter would be to give any enemy a mass and adapt the movement/gravity from there. The elegance of this solution is questionable however, since large aspects of the behaviour might change depending on the values of its parameters, and of course there are many instances in which such a simple solution is not possible.

By now I hope it is clear that there is no proper way of modelling the situation with inheritance only. This is not the only limitation of inheritance, but it is by far the biggest and most obvious. Using components will solve this issue, but also have some other clear advantages over this approach.

The solution

I have been talking about components a lot without really defining what they are. In fact, components are just small parts of a behaviour of a game object. Some examples of what could be components are: a sprite renderer, a collider, a health pool, and a mass. Instead of inheriting behaviour from a base class or explicitly implementing it, the behaviour of a game object can be entirely based off these components. The mass just applies a downward acceleration every frame, the sprite renderer draws a sprite every frame, and so forth.

Let’s revisit the original model: A bullet is merely a game object with a sprite and a collider. A player has in addition to that a gravity component (or mass) and a movement component, and so do enemies. Of course each type of object could have its own specific behaviour. It is very likely that every type of object will also have its own component: in our case we would most likely have a Player, Enemy, and Bullet component that takes care of all behaviour that is not shared with any other type of component.

classdiagram3
In this class diagram types of game objects are shown using ovals, but remark that there is no need for separate types for each type of object, since they are purely a game object with a specific set of components.

Now adding flying enemies is as simple as switching out components. We plug in a different kind of movement and remove the gravity component.

The technical side of things

While a lot of languages have trivial support for inheritance, components are something you have to do some work for to get started. Once again, I would like to refer to this GitHub repository for a complete implementation.

We will look at a very basic implementation. I will assume that components only have an Update method. In practice you might want a Draw method as well and/or some OnStart, OnDestroy, etc. methods. I might get back to this in future posts, but the implementation is not the focus of this post.

Further, I have added a minimal set of properties to the game object. It makes sense that every game object has a position, rotation, and size. To have types to represent these properties I have added a reference to the OpenTK library which I have been using for several projects, but the types could be trivially replaced by those of your game engine/library of choice. Observe that the properties have been made public. While this is wrong from an encapsulation standpoint, it is the easiest way of giving access to the properties for the components, who need this access to be able to actually change how the object behaves. An alternative is to pass the properties by reference to the components in the actual update call, but this grows increasingly more complicated when your components more complicated. This is therefore a “necessary evil” to make components work.

Now, let’s first look at the components. They actually are pretty simple:

public interface IGameComponent
{
    void Update(float dt);
}

Exactly, the only thing we need is a contract about how we can call the Update method. In C#, this is done by implementing an interface.

The interesting things happen in the GameObject class. Since this one is a bit more complicated, we’ll start simple:

public sealed class GameObject
{
    private readonly List<IGameComponent> components = new List<IGameComponent>();

    public void Update(float dt)
    {
        foreach (var c in this.components)
            c.Update(dt);
    }
}

The first interesting thing is how I made this class sealed. This means that you forbid any class from inheriting from it (in a sense the opposite of abstract). I did this on purpose, since I wanted to show that by using components, each game object is equal: an instance of GameObject. It’s the components that make it behave the way it does.

The rest of the class is pretty boring. We have a list of game components which we call every frame.

Of course we will want to have a method to add and remove components. These are pretty easy to add:

public void AddComponent(IGameComponent component)
{
    this.components.Add(component);
}

public bool RemoveComponent(IGameComponent component)
{
    return this.components.Remove(component);
}

Up until now we have assumed that we know the components. Now let us think about this: what if we want to access a specific type of component without knowing its instance? For example, let’s say we want to invert the gravity on all the game objects: We would have to get the Mass component from each game object and multiply its gravitational constant with one. The following methods are going to be really helpful with this:

public T GetComponent<T>() where T : IGameComponent
{
    return (T)this.components.FirstOrDefault(c => c is T);
}

public T[] GetComponents<T>() where T : IGameComponent
{
    return this.components.OfType<T>().ToArray();
}

Note that I use the default System.Linq library to make these methods very simple. Even if you haven’t seen the methods before, I assume that the result of theses methods are very clear. Both methods also have very predictable output if a component of the specified type is not present (null in the first method, an empty array in the second).

Since we already got started with generics, why not add the possibility to turn off gravity altogether?

public int RemoveComponents<T>()
{
    return this.components.RemoveAll(c => c is T);
}

To make things even easier, we can even make a generic method to add components.

public T AddComponent<T>() where T : IGameComponent, new()
{
    T ret;
    this.components.Add(ret = new T());
    return ret;
}

Since components will generally be very simple and not have constructor parameters, this method makes for a very elegant way of adding components to game objects, without ever having to keep around instances yourself.

Altogether, this gives a very simple game object that can be used to bookkeep its components and use them to decide its behaviour. An important note is that the provided implementation is very simple and all but perfect. One could for example consider to use a Dictionary<Type,IEnumerable<IGameComponent>> to have quick access to the game components by type. A big disadvantage of this is that if you start using inheritance in components (more on that later), you have to add a method that specifically adds for a type of component and all its children. In my simple tests I have found to get almost a factor 10 improvement in getting components that way though. Even though game objects will probably rarely have more than a dozen components, these are considerations you could make if performance is an issue.

How to use components

Even with a framework provided, it is still a question when and how to use components. This is more of a personal choice, similar to how you would decide for yourself when to use inheritance or not.

If we would take the “one class, one responsibility” approach, we should create a component for every bit of behaviour. While this is a completely viable approach, it is probably not the most convenient. This is why I personally would opt for a more hybrid approach: I take all the components that I can see being reused in other game objects and give them their own component. Behaviour of which I know that it will most likely be unique to one type of game component usually gets its own component. Of course this doesn’t have to be a component and could just be integrated in a subclass of GameObject, but I like to separate behaviour and component management and feel better knowing that my GameObject will always behave the same, since it is sealed.

It can be difficult to estimate when exactly to introduce a new component or use this unique component. One way or another, you will probably end up using an agile approach for this, whether you start with putting all code in the unique component and extract code from there, or just introduce a few additional components which you might end up only once.

The interesting thing about making new components for every new bit of behaviour you implement is that component reusability is not limited to a single game. If you decide to make another game in which gravity is a thing, you can easily reuse the existing components you wrote.

Components in Unity

I have mentioned Unity before. Games made in Unity – a game engine for 2D and 3D games – heavily rely on game objects that have a set of components, which define how a game object behaves. When I first started Unity, I ended up giving each type of game object its own script that would define its behaviour, but over time I learned how component-based game development has its merits, and since then I have developed several components that can be applied in different games.

Unity Inspector

The interface of Unity clearly shows its focus on components. Above you can see a part of the interface describing an alien in my latest Ludum Dare entry. It has its unique Alien script, but this is only a small part of the behaviour of the game object. Many components are reused by other objects, and the parameters you can edit on these components from the interface make it easy to adjust the behaviour to fit your object.

Unity provides a large amount of basic components: sprite renderers, colliders, audio sources, etc. In addition you can write your own components as scripts. Unity supports UnityScript (JavaScript), C#, and Boo for these components. Game objects in Unity adhere to a very similar interface as the GameObject class we wrote earlier.

Unity Scripts

In the screenshot above you can find an overview of the scripts I used for Satellites vs Aliens. The top section shows the scripts I wrote for the game specifically, and many scripts are used by multiple components. The bottom section however has some basic scripts that I wrote during other games and could import easily in this game to save myself some work.

I will not go into explaining how to write your own components for Unity, since there exist many good tutorials on this topic. It is however important to understand the ideas behind components and how these can be used in Unity to improve the way you use scripts.

And what about inheritance?

I have been trying to make a strong case about using composition and specifically components over inheritance. This does not mean that inheritance is something to be completely set aside. Some systems might not be very suitable to be implemented using components.

Composition and inheritance also are not mutually exclusive. In fact, the components can definitely benefit from the use of inheritance. Consider for example a Collider component, we might want to create subclasses for this to deal with different kind of colliders (BoxCollider, CircleCollider, etc.). Yet, if we would design our component framework properly (e.g. using a Dictionary<Type, IEnumerable<IGameComponent>> could get you into problems here), we could ask a game object for any collider and do something with it.

By using both composition and inheritance in conjunction, a very solid yet flexible framework can be built. The art is to balance when composition and when inheritance should be used.

Final words

In this post I have introduced component-based game development. While it is not a completely new method, I hope I have explained why components have benefits over inheritance-based approaches. I also think developers using a framework like Unity would very much benefit from better understanding the ideas and logic behind components.

Even given a component framework, using it is not trivial. Not unlike most of the programming, there is usually not one right way of doing it and how exactly your code ends up looking is usually defined by personal preferences.

By the same logic, components are not the right way of developing games and it definitely has several drawbacks: encapsulation becomes more complicated and if you don’t have a framework that already does all the bookkeeping for you, the initial setup takes a bit more effort. In the end I think components are worth the investment of setting it up and getting into the habit of thinking with components.

Next time

Today we only scratched the surface of components. Especially on the technical side there are still many open questions. In the next post I will discuss how components can communicate between each other, whether within the same game object, or not.

If you want me to spend some more time on any specific topic within this subject, or maybe focus more on how I apply components in for example my Unity games, feel free to let me know, since there are still many things to explore.

Place comment

Your email address will not be published. Required fields are marked *