Object decomposition in Druidstone

Warning! From time to time we are going to post some very technical material in this blog. This is one of those posts. Read on at your own risk.

Preface

Modern games tend to create new game objects by compositing them from separate reusable components. This is a very powerful concept as complex behavior can be built from relatively simple building blocks. Components can be things such as models, lights, animations, sound emitters and gameplay related components such as health and item components, just to give you some examples. In this blog post I’ll talk about how we use components to build the game objects in Druidstone.

The Dark Ages

Let’s start with some attempts that don’t work. Back in the 90s when C++ was hot and object-oriented programming was still considered a good idea, somebody had a great idea: hey, let’s build game objects using inheritance! Base-classes would be something like Weapon, Enemy, Player, Light or Vehicle. So if you needed an enemy that had a weapon and carried a light source you would create a new class and inherit from the necessary base-classes. Your typical textbook OOP solution. Needless to say this quickly led to a huge mess and to an explosion with the number of different classes.

Ok, what if you don’t use inheritance and just make the different components of the object direct data members of your class? Nope, this still doesn’t work because you end up with a new class for every combination of components in your game. Fast forward ten years. What you really need is some sort of entity-component-system where the components are decoupled from objects and game objects are no longer classes. What you have instead are “entities” which are usually just numeric IDs and components which are linked to these entity ID. This way components are maximally decoupled.

Something like this:

struct ModelComponent
{
  Vertex* vertices;
  int* triangles;
};

struct LightComponent
{
  Vec3 color;
  float brightness;
};

ModelComponent models[MAX_ENTITY_ID];
LightComponent lights[MAX_ENTITY_ID];

This kind of component system is how many game engines work. This is also basically how my previous codebase powering Legend of Grimrock 2 was structured (although the components were written in Lua, not C++). However, there are some problems. Inter-component communication and dependencies are kind of hard. With the dark age inheritance model you could at least call the methods in the same class without a fuzz. Not so with a component-entity system. If component A wants to talk to component B, or just do a simple thing such as look up a value in the other component, first it has to know the entity ID and look up the other component using the entity ID before it can do anything. This has to be done for every component look up. And believe me, there are many, many of those look-ups in a game! If this sounds too slow or cumbersome you can start adding coupling between the components by storing direct references to other components in your components… and you’re back to square 1. This direct coupling is what you wanted to avoid in the first place.

Enter Lua

In Lua you just have a single data structure, the table, which is basically a nifty, optimized hash map. So can we do better in Lua? Well, glad you asked! Sure we can.

First let’s throw that useless object-oriented garbage away ([1] and [2]). Once you realize that you no longer have to couple data and functions all sorts of fascinating possibilities start to emerge. Since you can dynamically add and remove table fields, why not just stash data for all of your components of a game object in a single table? Accessing another component’s data becomes trivial since it’s sitting right next to you in the same data structure. Once you have this then you can just use free functions to operate on your components and you can pass those object data tables around from function to function. No need for entity IDs.

A simple example:

local t = {} -- create a new empty object

-- create some components for t
init_model(t)
init_light(t)

-- call a component function
fade_light(t, 10.0)

-- and here is the implementation for the components

function init_model(obj)
  -- init state for model component
  obj.model_var1 = ...
  obj.model_var2 = ...
end

function init_light(obj)
  obj.light_var1 = ...
  obj.light_var2 = ...
end

function fade_light(obj, target_brightness, time)
  -- do whatever fancy stuff is required to fade light's brightness to target value
end

This 80s style no-nonsense programming is pretty much how objects in Druidstone are coded. Really, really simple and also reasonably efficient. There are just a few things to keep in mind. Since all components store their data in the same table, you want to avoid clashes with field names. A simple naming convention by prefixing the component name works like a charm. Secondly, you can have just one instance of a component per object. E.g. you can’t have two model components in the same object (unless you implement the multi model stuff directly into your component).

I could argue that in practice allowing multiple components of the same type per object is not so good idea. It’s quite expensive and unneeded special case for most objects. In those rare cases when you really need this, it’s usually better to just have multiple objects and perhaps link them somehow. The component system in Grimrock 2 allowed arbitrary number of components per object, but the price was that every component was stored in a separate table and every component had to be named so that when you referred to an component in an object, you always had to specify the component name. This system was very flexible but also more complex, less efficient and harder to use.

All in all, we have been building Druidstone with this new system for a year now and we’re happy to report that no major kinks have been found so far and working with the codebase has been a joy.

I hope this article was an interesting read. Thanks for reading!

Petri

[1] Wikipedia OOP criticism

[2] What’s wrong with object-oriented programming

Petri Häkkinen

Developer

10 Comments:

  1. I’m wondering why going back to “C OOP” style naming, when instead you could use compositing (which can also solve the “multiple components” problem):

    local t = {} — create a new empty object

    — create some components for t
    t.model = model.load(…)
    t.lights = { light.create(…), light.create(…) }

    — call a component function
    fade_lights(t, 10.0)

    — and here is the implementation for the components

    function model.load(…)
    — init state for model component
    return { var1 = …, var2 = … }
    end

    function light.create(…)
    return { var1 = …, var2 = … }
    end

    function fade_lights(obj, target_brightness, time)
    if obj.lights then
    for _, l in ipairs(obj.lights) do
    — do whatever fancy stuff is required to fade light’s brightness to target value
    end
    end
    end

    • That’s pretty much how my previous codebase worked. The problem is that it requires having a separate table for each component and in many cases requires an extra indirection when accessing the fields, which I wanted to avoid. Just having a single table for storing all components of an object helps with garbage collection (less objects and references for the GC to track).

  2. Simple and interesting!

    I’ve done some entity-component systems in the past, both in Java and C++, and while a system like that is relatively simple and straightforward to implement, this table-based method sure beats the amount of boilerplate in a more verbose language…

    Do you have any control of the memory layout this way, for stuff like SIMD operations etc?

    • I’m glad that you found the article interesting! Lua is a pretty high level language so there’s no access to SIMD, memory layouts or anything low level like that.

      That said, I’m using C / C++ for speed critical things (mostly just rendering nowdays). When I’m working on that area of the codebase, I’m more concerned about data layout, caches, etc.

  3. Wow, so you too! 🙂

    First of all, thanks for a very good read, Petri!

    It’s interesting how after 3 years of development on our game, we also came to a conclusion that this “80s no non-sense” style of coding is a really good fit for games. And I’m noticing this trend in more coding posts than before.

    Just have functions that operate on components and possibly call other functions. So you can have what I call “atomic” functions that do the simplest stuff (like SetPosition), then middle functions that use these atomics and do something on their own (like TeleportActor, which certainly uses SetPosition), and then complex high-level functions that sort of compose their behavior out of many other functions.

    When coding ECS in my Lua projects for Love framework, I was always skeptical to go this single-table approach with name-spacing the fields. But in reality, we’re making games, not public APIs, so as authors, we know what we’re doing, and so we can easily get away with this approach. The simpler the merrier, right?

    A question pops up though. How do you separate the fields in the GameObject inspector if there is no logical separation?

  4. One more question.

    Where do the “component” functions live? Do you separate them into “modules” by category, or do they just live all together in some globally accessible table?

  5. Sorry for the spam, but you really got me on for this! 🙂

    How do you update the GOs? I suppose there is a function for every component that needs to update every frame and you just call these functions in the required order in the main loop.

    function update_game()
    for k,v = pairs(components) do
    update_movement(v)
    update_light_flicker(v)
    update_whatever(v)
    end
    end

    But then every such function would have to check if the component is present by checking for one of the fields. Which ain’t optimal. I’m intrigued how you solve this.

    • Thanks for the comments and interest in the article, Matej! Here are the answers to your questions:

      > How do you separate the fields in the GameObject inspector if there is no logical separation?

      I set up the gui for each component manually. E.g. gui_light() implements the inspector for the light component of an object. It’s just a single line of code per field using imgui.

      > Where do the “component” functions live? Do you separate them into “modules” by category, or do they just live all together in some globally accessible table?

      They are just global functions. I guess I could put them in modules, but ehh… globals work just fine for me.

      > How do you update the GOs?

      Each system updates their own components. E.g particle system components are stored in their own array and update_particle_systems() iterates the particle system array and does whatever is needed. This is actually a big win compared to classical inheritance based OOP solution, because there is no need to iterate objects without the specific component and there’s much fewer function calls.

      • Thank you for the reply!

        > Each system updates their own components. E.g particle system components are stored in their own array and update_particle_systems() iterates the particle system array and does whatever is needed.

        I see. So I guess when you call init_particle() (or any other init_component() function) on a GO, the function also adds that GO to the array of particles which the particle system then iterates on, using only the “particle_something…” fields.

  6. About the OOP criticism – I share that opinion stated there too. Digging into a .NET framework based website, it’s a horror to see ‘at once’ what’s happening.

    I’ve never seen/done anything more transparent and easily investigatable by using old school ASP/Javascript, with some XML and created the ‘objects’ myself (code snipplets) that get included in an agile manner and output different content depending on what parameters were given while calling them.

Leave a Reply

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