top of page

Custom C++ Game Engine

github.png
gameEngine.jpg

During my second semester at FIEA, I built a custom C++ game engine from scratch. This project allowed me to grow my knowledge of C++ exponentially, while also understanding how a commercial game engine functions. It also helped me get comfortable with modern C++ syntax, copy/move semantics, STL containers etc. Developing a game engine also helped me:-

  • Enhance my understanding of modern C++, object oriented programming, raw pointers and the need for smart pointers.

  • Understand in-depth about how C++ STL containers work by writing custom versions of them.

  • Maintain a large codebase and addressing any cascading design and/or logical errors.

  • Develop a custom scripting language to deserialize and construct objects at runtime from raw data using JSON.

  • Familiarize myself with visual studio's unit testing framework to verify code functionality.

  • Implement and understand a variety of design patterns including the iterator pattern, the chain of responsibility pattern, the observer pattern and the singleton pattern.

  • Implement runtime polymorphism using inheritance and function overloading, and compile-time polymorphism using templates.    

Custom STL Containers

The first few components of the game engine involved engineering custom versions of the C++ STL containers, namely forward_list, vector and unordered_map, in order to store and retrieve data.

Building these custom containers helped me:-

  • Understand and implement C++ copy and move semantics.

  • Implement compile time polymorphism through template programming.

  • Understand how STL containers work under the hood.

  • Implement the iterator pattern by making iterators for each of the custom containers.

Hierarchical Data Structure

The next step involved forming a hierarchical data structure that resembles the scene hierarchy in Unity or Unreal. This data structure is called a Scope and is basically comprised of a HashMap of string-Datum pairs. A Datum is a runtime polymorphic version of Vector and can store ints, floats, strings, vectors, matrices and pointers to other Scopes. So the Datum&Scope classes form a recursive pair. Also each Scope has a pointer to its parent, this forms a tree of Scopes. 

This allowed us to create an derived class called Attributed, that has a set of pre-determined pairs with specified names and types, which closely resemble attributes on game objects within Unity or Unreal. 

These pairs are populated at runtime by deserializing JSON data.

JSON Object Parser 

In order to populate each Attributed object's attributes, we needed to write a JSON parser that does so in a data-driven manner. The JSON parser was built by implementing the chain of responsibility pattern where we have a single parse coordinator which manages several helpers which respond to requests made by the parse coordinator as to whether or not they can parse a particular type. 

The JSON parser helped me gain experience working with push-down automatas, as this was the mechanism used to maintain structure while parsing the JSON root object.

   

Factory System

Now that we had a mechanism to construct Attributed objects at runtime through JSON deserialization, we needed a mechanism to identify the type of Attributed derived object to construct. This involved us changing the grammar of our scripting language to include the name of the Attributed derived type of the object to be constructed. Once this information is parsed, we call the Create function on our Factory system which creates and returns the object of the passed in type. The Factory class implements the factory pattern as well as the singleton pattern.

   

GameObject & Action

At this point our Scope system resembled the hierarchical scene structure seen in Unity or Unreal. This led to the creation of a GameObject class which derives from Attributed and has prescribed attributes of position, rotation, scale, and a name. It also has an update method which calls all it's children's update methods on top of running any additional code we put in. This allows us to have a singly rooted scene or a root GameObject.

Similarly we made an Attributed derived called Action which executes a set of instructions that can be written in the JSON scripting language.

ActionList extends Action to contain a set of Actions and executing one or many of these contained Actions based on conditions. These conditions are also defined on the JSON side. This allowed us to make scripting versions of control flow statements such as if-else statements, switch statements etc.

   

Event System

Every game engine has some sort of event system for various purposes, one of them being input. Events help us avoid paying the cost of polling every single frame which can be quite expensive. In order to implement the event system, we had a list of event subscribers and event publishers. An object can become a publisher or subscriber by simply implementing one of the IEventPublisher or IEventSubscriber events.

We also had an EventQueue entity which maintains a list of all EventPublishers in the scene and checks if they are expired every frame. If they are, then the deliver method is called on them which cycles through the list of subscribers and delivers the message payload to them. After this the expired event are either requeued or removed from the EventQueue based on whether or not it's a looping event.

The event system helped me understand how to implement the observer pattern.

   

bottom of page