CUSTOM GAME ENGINE

Status

Start Year

Duration

Development Platform

Technologies / Concepts

Complete

2023

5 Months

Native C++

C++, Data-Driven Design

OVERVIEW

A large portion of my graduate studies involved developing a game engine using Native C++. Having mainly coded in C and C# up until now, this was my deep dive into C++.

To build this engine, I first started by creating my own implementation of basic data structures such as Singly Linked Lists, Vectors, and Hash Maps. Although this was not particularly necessary for the project, it gave me better control over the exact functionality of these data structures as well as experience implementing them.

The engine was designed to be able to take user input through JSON input files and parse this at runtime into a Game Object Hierarchy.

To do this, I utilized the Chain of Responsibility pattern using one main parser that would then iterate through helper patterns until one was found that could parse the given input data.

Combining this with Run-Type Type Instancing (RTTI) allowed for users to define objects externally and the engine to then dynamically implement them at run-time.

This also allowed the addition of User-Defined Objects and Grammar that could then be easily appended to the engine.

Sample JSON Input

Once the objects had been read in using the parser, they had to be instantiated. However, allowing the user to add custom data types meant that the creation of these objects had to be facilitated by Templated Factories.

By templating these factories, the template argument would then be the type of Object created by that factory.

All data types contained in the game hierarchy would then have to be one of the base data types (int, float, etc.) or inherit from GameObject.

GameObject was an inheritable class that many basic game elements implemented. GameObject contained much of the base functionality needed for all basic objects in the hierarchy.

Some such functionality included a list of Action objects to call, a list of Child GameObjects, as well as an Update method that would also call the Update method of all of the child GameObjects.

Templated Factory Pattern

The engine also contained other standard functionality, such as an Action system.

Action was an Abstract Base Class that would be triggered, by default, on a GameObejcts Update method.

Action could then be implemented in many different ways depending on functionality. An “Add” action could add a value to a specified target, even one found by looking upwards in the hierarchy. Another example was an “If” action that would contain a condition as well as a list of actions. This would mean that when Update was called on this action, the child actions would only be called if the condition was true.

To further increase communication between GameObejcts, I implemented an Event System. Through utilizing the Observer Pattern, GameObjects could subscribe to events of a specified type and be notified when the were triggerd.

Two Actions were added to integrate the Action system with the Event system. “Reaction” allowed a list of Actions to only be called when a specific Event was triggered, allowing Actions to respond to Events. “ActionEvent” then did the opposite; calling the Update method on this Action caused an Event of the specified type to be queued.

Event Object Header File

Unit Tests for Singly Linked List

WHAT I LEARNED

This was both the most intensive, as well as the most educational project I have worked on to date. Though each stretch of the project required many late nights to complete, each portion helped to give me a broad and deep understanding of C++.

From data structures all the way up to high level concepts like event systems for user input, being able to construct a game engine from scratch gave me an understanding and appreciation of the complexity of what it takes to make an efficient, yet scalable engine.

Working with a code base this large necessitated the use of Unit Tests. Each section of the code has been thoroughly tested detecting for accuracy, memory leaks, as well as for code coverage to ensure no line goes unchecked.

This project was an excellent help in my journey of writing code that is efficient, expandable, and has the desired functionality as well as taking the steps and tests necessary to make sure the code is doing so.