Goodbye Inheritance

Solution for a problem we inflicted upon ourselves
Date
Updated
Author Fix License CC0

Introduction

Object-Oriented Programming has been present in game development for a long time, and it’s not going away any time soon, no matter how much I wish it would. Chances are, I cannot convince you to fully give it up, but I’m going give an example from an OOP-less world, present a case for why you should use less inheritance in your code and offer a more flexible alternative.

A Familiar Scenario

Let’s say in your game you have 2 types of entities. One entity is a warrior, whose main role inflicting damage and guarding areas. Another entity is a builder, whose main role is constructing buildings. They both inherit from the same class, but the Builder cannot attack and Warrior cannot construct buildings.

             Inherited by ┌─────────┐
           ┌─────────────►│ Warrior │
┌────────┐ │              └─────────┘
│ Person ├─┤
└────────┘ │ Inherited by ┌─────────┐
           └─────────────►│ Builder │
                          └─────────┘

Now, let’s say, you wanted to introduce a Skirmisher, a unique unit that can attack and construct buildings. From whom should the Skirmisher inherit? If it inherits from the Warrior, you’ll have to duplicate the construction code and if you inherit from the Builder you have to duplicate attack code. In either case you maintain the same code twice. This is not end of the world, but can get complicated the more types of characters you add. This type of complexity is also very inflexible, with each new character type requring a lot of template and copy-paste work.

             Inherited by ┌─────────┐
           ┌─────────────►│ Warrior │
┌────────┐ │              └─────────┘
│ Person ├─┤
└────────┘ │ Inherited by ┌─────────┐
           └─────────────►│ Builder │
                          └─────────┘
        ┌────────────┐
       ?│ Skirmisher │
        └────────────┘

Who is Skirmisher’s most suitable parent?

If you’ve made even just one game while following OOP, you’ve encountered this situation, probably multiple times. But there is a very simple way to avoid this.

One Solution

I’m only mentioning this because some developers I discussed this with brought this up as a first solution: Interfaces. They partially solve the inflexibility problem - if we ever needed a new entity type, we can simply inherit from the interfaces with desired behaviour.

                          ┌────────────┐
             ┌───────────►│  Warrior   │
┌──────────┐ │            └────────────┘
│ IWarrior ├─┤
└──────────┘ │            ┌────────────┐
             └───────────►│            │
                          │ Skirmisher │
             ┌────────────┤            │
┌──────────┐ │            └────────────┘
│ IBuilder ├─┤
└──────────┘ │            ┌────────────┐
             └───────────►│  Builder   │
                          └────────────┘

Unfortunately, they are available only in some languages, for example, C# has them, but C++ does not (yay for inheritance hackery and vtables!). But they can be unwieldy when you don’t know what interfaces your entity is inheriting and even worse: it can be slow. Attempting to cast then checking against null can be computationally expensive, which is what we obviously want to avoid in a game that has to run at least at 60 frames per second. While this is a step in the right direction, but I have a more flexible and usually faster solution.

A Better Solution

A solution that works for more languages is simple: enumerator bitflags used as tags. Think of our game loop as a data-processing programs, and the entities as the input. We have one object - Entity. Inside the Entity we define a variable to serve as our bitflags. To follow the previous example: the WARRIOR and BUILDER flags.

enum EntityFlags : uint32 
{   // All values are power of 2
    PERSON = 1,  // 0b0001
    WARRIOR = 2, // 0b0010
    BUILDER = 4, // 0b0100
   /* other flags */ 
};

struct Entity
{
    EntityFlags flags;
    /* Other fields */
};
Whenever we need a specific type of entity, we just assign the needed combination of flags.
Entity create_builder()
{
    new_entity = make_entity();
    new_entity.flags |= BUILDER;
    /* other definitions */
    return new_entity;
}

Entity create_warrior()
{
    new_entity = make_entity();
    new_entity.flags |= WARRIOR;
    /* other definitions */
    return new_entity;
}

Entity create_skirmisher()
{
    new_entity = make_entity();
    new_entity.flags |= BUILDER | WARRIOR;
    /* other definitions */
    return new_entity;
}

Later on, when we are processing our entities, we check for flags, and perform the desired behaviour. Coming up with new types of entities is easy - we just assign the combination of flags needed for desired behaviour. If we have new behaviour, we can add it into our loop.

for (entity in all_entities)
{
    if (entity.flags & WARRIOR) { /* Check and do Warrior behaviour */ }
    if (entity.flags & BUILDER) { /* Check and do Builder behaviour */ }
    /* other flags */
}

A neat benefit of this approach is that we can modify behaviour during runtime. Let’s say we want to create an entity can be upgraded, it begins as a warrior and later learns to build. This is very easy!

void upgrade_entity(Entity *entity)
{
    entity->flags |= BUILDER;
}

As you can see, tags are very flexible, and due to the simple nature of bitflags, they can be pretty fast too!

You don’t have to use your flags to track just entity types. Entity states such as ALIVE or even whether it should be affected by certain systems, like STATIC or PHYSICS_ENT. You got up to 64 bits you can store in one variable, use them as you wish!

Of course, there are many ways to improve upon this, architecture and performance-wise, but this is just a simple example to help you visualise the idea. As with everything, you should adapt how you use this principle to your specific case to maximise benefits.

Thanks

I would like to thank Alex Reed, Duka and No Need, for helping me polish some of the rougher bits of the article!