top of page

Engine Development

About the project

During my second year at The Game Assembly (TGA), we programmers were assigned to create a custom Game Engine from scratch. We weren't expected to create a full editor. Our main goal was to be able to import levels from Unreal Engine, and run those levels in our own engine.

​​

Development on the engine began about a month before Spite and has continued actively since then.

​

My primary responsibility was working on the level importer and maintaining it throughout development. We received a level exporter from TGA that read all data from the scene and exported it in a .json format.

​

Later, during the production of Sub Umbra, I was also tasked with implementing a Visual Scripting Editor.

​​​​

Key Contributions

​​

  • Level Importing

    • Level parsing ​

      • Parsing of level data from .json files, converting the data into our engine's objects​

    • Binary level loading

      • Converted level data into binary data for faster loading times.​​​​​​​

  • Visual Scripting

    • Updated an ImGUI-based visual scripting tool​

    • Codebase Familiarization​​

      • Integrated an existing codebase developed by a different team into our in-house engine.​

    • Undo & Redo system

      • Designed and implemented a undo/redo system for editor interactions.​​​​​

    • Node Creation

      • Created multiple types of nodes for visual scripting​​​

    • Actor System Integration

      • Added support for interacting with the components of the actors in the level.​

    • Interconnectivity between Script Graphs​​

      • Script Graphs can send and receive events to on other graphs.​

    • Logging system​​

      • Logging system for the Script Graph Editor to notify the user of various commands or actions.​

    • Autosaving system​

      • System for displaying and creating autosaves.​

  • Asset Management

    • Helped setup the structure of our content root, used symlink for easier connectivity.​

​​

​​

Level Importer

The first step was setting up level file export/import. I created a dedicated folder in our content structure for storing level .jsons and designed the level load function to locate the file by name.

 

Next, I implemented .json parsing to extract object data such as name, type, and transform.

​

Handling the transform was more complex, since it had a different setup than our engine. The coordinates in unreal are stored as XZY, but in our engine they were stored as XYZ. The rotations were also an issue since it had a different format than our engine. Fixing and converting the rotations took some time since at the time we didn't know that the rotation implementation in our engine incorrect, but we got it after some tries.

​

The level exporter provided by TGA handled some default Unreal components, such as Mesh Components and Skeleton Components. For custom components, I had to manually register each one, using a visual scripting event provided by the exporter.​

​

The custom components also had the possibility to store custom variables which enabled our level designers to assign custom values to objects.

​

​The exporter stored the components as strings, which the system then reads and creates the correct component for the actor.

​

After the actor was done loading, it was then added to the level in engine, and the parser continued on to the next one.

​

When the parsing was done, level initialize was called, and after that, the tick & render loop started.

​

Binary Level Loading

During the development of Spite, we noticed that the level loading times sometimes were quite slow. I didn't have time to address the issue during that project but I decided to tackle the issue at the start of Sub Umbra

 

The main bottleneck was the .json parsing. Parsing .json files, specially large files containing entire level data, was quite slow.

​​

Linus Erkfeldt, a fellow programmer, had previously created a binary serialization tool, that took data and converted it into .rasset files, our engine's binary file format.

​

The asset management system was built around this. First an asset is loaded normally, then serialized into binary data. On subsequent loads, if the original file wasn't changed, the engine loaded the binary version instead, resulting in a significant performance boost.

​​

To integrated this with the level importer, the parser first completed parsing a level as usual, creating all actors and categorizing them by type. 

​​

Afterwards, the system would iterate through each category and generated a .rasset file for that type.

Having them being separate files gave us the posiblity to thread it later if needed.

​​

The level importer function was updated, to check which version of the file, .json or binary was newer. The engine would then load the most recent version. This optimization reduced loading times from roughly 45 seconds to just 3 seconds. The loading times increased after more content was added, but it was still way faster.

​

The only issue was that the speed boost only applied subsequent loads, as the binary files needed to be generated once from the .json data.

​​​​​

Visual Scripting

For Sub Umbra I was tasked with developing a Visual Script Editor.

 

​We received a basic Script Graph Editor from The Game Assembly. It did have basic functionality like creating and removing nodes and pins, creating variables and having a begin event node.

image.png

Undo & Redo

My first priority was to improve the user experience.

​

One of the key features was an Undo & Redo system. This took some time since the codebase was large and entirely new to me.​

​

While working on this I also implemented a copy and paste system, since it felt like the most common function and one of the most useful ones.

​

The Undo & Redo system had support for:

​

  • Creation and removal of nodes

  • Creation and removal of pins

  • Copy & Paste

​

There were plans to add support for Undo & Redo for node movement. However I didn't have enough time to  implement it, since getting familiar with the codebase took quite a bit of time.

​

VisualScriptingUndoRedo.gif

Logger System

I created a Logger class which had support for different levels of messages, such as warning, error, success or just plain logs.

​​

The logger class was a singleton, which made it accessible from anywhere. I added logs to important functionality, such as the redo & undo system, and warnins for autosaves.​​

​

{F506ABD7-34B1-44BD-A4E9-FB323A9B49E2}.png

Load & Save System

I implemented an improved version of the load and save system. The one in the original codebase only had support for a single save file, but I added support for autosaves and keeping a history of up to 10 autosaves.

​​

I also implemented file explorer dialog, using a wrapper from Linus Erkfeldt,  to manually select a save file.

​

I also gave the user a button to quickly reset the script graph.

{CDCAD63C-354D-4F47-9948-35A2687DDC23}.png
image.png
{9FC4E735-9EEE-4244-AE1E-9B606CF3B61C}.png

Node Creation

When creating the nodes, I tried to maintain consistency with keeping Unreal's naming conventions and functionality, since it was our Level Designers that would be using this tool, and they were more familiar with the Unreal system. Some of the flow control nodes I implemented included:​

​

  • While & For Loops

  • Branch

  • ​Do Once

  • Sequence

  • Switch

  • Timers

​

I also added interconnectivity between the Script Graphs by creating nodes such as "Receive Event" and "Call Event",  which would enable communication between different script graphs.

​

Additionally I created event nodes such as OnTriggerEnter and OnCollisionEnter, to connect the Script Graph to our physX system. This gave our level designers the ability to create trigger boxes and bind them to different script graphs.

Actor Integration

I assigned an internal variable to each script graph a internal. The variable held the ID of the actor the script graph belonged to.


This made it possible for the script graph to access the actor and subsequently access the actor components to call different functions.

​​

Additionally, the OnTriggerEnter and OnCollisionEnter nodes sent a payload to the script graph, containing the ID that interacted with the owner actor. This feature made it possible to interact with the other actor.

Engine Retrospective

Having worked across nearly every area of game development, I found that Engine and Tools programming was by far the most enjoyable for me. Unfortunately, due to time constraints, I wasn't able to refine and expand these systems as much as I would've liked, as I was needed in other areas during the development.

​

​

I am part of The Game Assembly’s internship program. As per the agreement between the Games Industry and The Game Assembly, neither student nor company may be in contact with one another regarding internships before April 23rd. Any internship offers can be made on May 5th, at the earliest.
bottom of page