Build a Game Engine

The UObject Base Class

Prerequisites

Article

This will probably be the shortest article on this site, so it seems fitting to be the first. In this article, we'll review the base class for all other classes in our engine: UObject. The UObject class is similar to the UObject class found in Epic's Unreal Engine and will help us later on to provide some basic reflection capabilities and with integration into our scripting engine.

Here it is:

class UObject {
public:
    UObject() { m_type.classID = 0; }
    virtual ~UObject() = default;
    struct Type {
        std::string className;
        uint32_t classID;
    };
    UObject::Type UType();
protected:
    Type m_type;
};

This is just a base class for everything else in our engine, so it won't have many properties or functions. However, you can see the first reflection method and associated data here - by calling the UType() function, it will give you the class name and a numeric class ID (unique to each type of class in the engine).

I am going to leave the implementation of this function for our articles on meta programming and reflection, since populating this data is not very straightforward. Instead, let's focus on why this class is so important:

  1. It's an easy way to add functionality to every other class in your engine. Later, when we cover meta programming, we'll build out some much needed reflection capabilities, since C++ provides none out of the box.
  2. When we look at adding scripting languages like JavaScript via V8, or C# via Mono, we'll want a single base class to pass objects back and forth. This matches the expectation of those languages - V8 has the v8::Object type and C# also has an Object base class. Declaring every other class as a subclass of UObject means that every other class can be downcasted to a UObject and will save us hours of manually writing function declarations.

In my opinion, many engines make the mistake of overloading the base class. I've gone back and forth, for example, on including Serialize and Deserialize functions in the UObject class. On one hand, not every object needs to be serialized; on the other hand, it's an easy way to make sure every object can be serialized. As always, how much you add to your base class will depend on your use cases and may change from project to project.

You'll see almost every class on this site is a subclass of UObject. Whatever you call your base class, by the time you create your 10th, 20th, or 100th class, adding : public UObject (or your equivalent) will come as naturally as breathing.