Component Based Game Engine From Scratch Part 2

Introduction

Last time I explained in detail how the whole system worked. This time I will focus on the component system, as it is quite complex. Using it isn’t complex, but I’m trying to mimic Unity’s functionality that was written in C#. Functionality that is partially implemented within the editor and doesn’t relate directly to typing in code. I have no idea how Unity really does this, but the solution I have come up with seems tidy enough and fits neatly within a single C++ header.

Overall Design

Game engines are tricky things to manage. They have code that needs to

  • Check for collisions
  • Play sounds
  • Manage user input
  • Draw on the screen
  • etc.

Except while these are separate and distinct pieces of code – the collision response code shouldn’t need to know anything about the sound playing code – unwanted coupling can happen. After all, somehow the game needs to play a noise when the player shoots their gun, or when a bullet hits a wall.

Death by managers

One way to do this is to have lots of global “manager” classes – an entity manager to manage the items in the game, a sound manager to handle game sounds, a collision manager to check if entities are touching, etc.

This is fine, if you like global classes in your code. If you don’t, you end up passing pointers to these manager classes deep within the codebase. You’re three levels deep in some awful game logic and need to see if the user has pressed the ‘A’ button? The code needs to either pluck a reference to the relevant manager from the global namespace, or hope a pointer has been passed through every single function up to that point.

With this kind of system though, everything ends up knowing about everything else. The physics code might not do anything with the sound manager, but it’ll still need to include its header and pass a pointer through itself just in case.

Component systems

The other way to make an engine is with a component system. Instead of creating lots of manager classes and passing them about, the game engine is chopped up into pieces. The physics goes in one class by itself, the sound goes in another. What were entities become mere shells that hold lists of components, and some glue to bind the whole lot together.

If you then organise game entities (which we’ll just call game objects now) into a tree structure, you’ve got a sort of scene graph, which gives you lots of extra benefits, too.

What I’m explaining in this post is how the glue works. How you can make the player emit a noise when they collide with something, without needing the physics code or the sound code to have any knowledge of each other.

It should be noted though that neither the physics nor sound code is a game component. They’re something different that I’ll write about in another post. Game components are for attributes that are associated with a game object, such as

  • Sprite definitions
  • How to represent this object within the physics system

What Unity does

Unity is a game engine, it’s a complex system with its own integrated editor and uses C# for its scripting. The game world is made from game objects, which can themselves contain other objects (think of a player carrying a gun, the gun would be a child object of the player). Objects can also contain components, components describe or define functionality the object has.

The whole engine is split into small pieces, each piece being separate from the rest. Adding new functionality then becomes a case of creating a new game object or component. Instead of deeply nested hierarchies of objects inheriting other objects, the system is quite flat with objects containing other objects.

So in Unity you can create a game object representing the player, and the attach a component that handles the physics, and another component that plays sound, and a component full of your own user-defined code. None of these know anything about each other, nor do they need to.

However, if your own code needed to access some data from another component, the code to do this is very simple:

using UnityEngine;  
  
public class ScriptExample : MonoBehaviour
{
    void Start()
    {
        // Disable the spring on the HingeJoint component.
        HingeJoint hinge = GetComponent<HingeJoint>();
        hinge.useSpring = false;
    }
}Code language: HTML, XML (xml)

From Unity’s Online Documentation

The nice part is GetComponent(). The code is very plain, very straight forward. No pointers to manager classes are being passed around the code, everything is nicely decoupled.

If you’re not totally put off by C++ templates, you can emulate something similar yourself…

C++ version

As explained in the previous post, my engine has two core classes that things can be derived from.

  • GameObject
  • GameComponent

GameObjects can contain other GameObjects, and can also contain GameComponents.

A GameObject has two lists, one for the child objects and one for its components:

std::vector<GameObject*> children;
std::vector<GameComponent*> components;Code language: HTML, XML (xml)

This code is part of the base GameObject header and is used to create and add a component to a game object.

template <class T>
T* AddComponent()
{
	// Is it even a component type?
	if (std::is_base_of<GameComponent, T>()) {
		GameComponent *tmp = new T();
		tmp->parent = this;
		//tmp->Init();
		components.push_back(tmp);
		return static_cast<T*>(tmp);
	}
	throw "That's not a component!";
	return NULL;
}Code language: JavaScript (javascript)

and this code is used to add a game object as a child

template <class T>
T* AddChild()
{
	// Is it even a GameObject type?
	if (std::is_base_of<GameObject, T>()) {
		GameObject *tmp = new T();
		tmp->parent = this;
		tmp->root = root;
		//tmp->Init();
		children.push_back(tmp);
		return static_cast<T*>(tmp);
	}

	throw "Can't add non GameObject as child";
	return NULL;
}Code language: JavaScript (javascript)

Using it is incredibly simple, here’s how I add a 2D sprite component to a game objectFirst I create a member variable to refer to the sprite later on

Teapot::Sprite2D *sprite;Code language: CSS (css)

Now, in the Init() function of the game object, I add this code

sprite = AddComponent<Teapot::Sprite2D>();
sprite->Init();
sprite->assetName = "test";

sprite->position.w = 16;
sprite->position.h = 16;Code language: PHP (php)

Now, without doing anything else, the Sprite2D component has been added to this game object. Automatically the Sprite2D’s Update() and Draw() functions will be called when needed.