Custom ECS Engine for Nintendo Switch

Developed in November 2020

C++ Cross-Platform Custom-Engine ECS Graphics Jenkins Nintendo Switch OpenGL UML Unit Testing


bannerImage
Custom ECS Engine for Nintendo Switch
Introduction
My Contribution
        Technical Design
        Core Architecture
        Unit Tests
        OpenGL Renderer
        Nintendo Switch
        Jenkins Setup
        Doxygen Setup
        Profilers
Similar Projects

Introduction

For the first project of my second year in university, I had to create a cross-platform 3D game engine from scratch with a team of programmers. We had all chosen a specialization for the project, mine being engine/tools programming.

Our team had to focus the engine on the tower-defense genre, support the Nintendo Switch, and create a small demo with it to showcase its capabilities.


My Contribution

Technical Design

As the engine programmer in our team, I led the process of designing the engine by concepting the core modular architecture and then communicating with each team member about what their specific module(s) required and how they could be structured.

UML class diagram showing the core architecture of the engine[br][br]It uses ECS to modularize every aspect of the engine. Individual modules are not fully represented in this diagram

UML class diagram showing the core architecture of the engine

It uses ECS to modularize every aspect of the engine. Individual modules are not fully represented in this diagram

Modules had their own Visual Studio projects and were built as static libraries. They would define their own components and systems that were put into the ECS at the core of the architecture. The application project would simply link against all these modules, add them to the game in the main function, and then start the game loop.

The reasoning behind this structure is that we could easily work on our own parts of the engine without causing conflicts. Modules can depend on one another, rendering with always need to know about transforms for example, but by having these modules be different projects entirely it becomes much more difficult to accidentally add a new dependency.

It also helped build times because modules can be built concurrently, less of them need to be rebuilt when changes are made, and the decoupling this structure enforces makes for more independent compilation units.

Table I made for the technical design document describing each module and their dependencies

Table I made for the technical design document describing each module and their dependencies

Dependency chart of all modules/resource handles

Dependency chart of all modules/resource handles

Core Architecture

Public interface of the Game class, which is where most core features come together (modules, event system, game pausing, etc.)

It is heavily commented in JavaDoc format, which is used by Doxygen


The Visual Studio solution we had at the end of the project

The Visual Studio solution we had at the end of the project

I implemented the core of the architecture I designed. This contained:

  • Module and ResourceHandler base classes and management
  • Game loop interface
  • Scene loading and switching
  • A wrapper around the EnTT library to add restrictions for how our specific project uses ECS
  • A global event system
  • Game pausing/unpausing
I also implemented some of the more basic modules that could be considered part of the core:
  • The config module
  • The transform module
I also created the Visual Studio solution and project templates for modules and resource handlers to make it easy for teammates to add these projects themselves since they would have required some setup otherwise and we needed a lot of them.

Project templates I made for each type of project we needed[br][br]These made adding new modules, resource handlers, and unit tests much easier for everyone

Project templates I made for each type of project we needed

These made adding new modules, resource handlers, and unit tests much easier for everyone

Unit Tests

The output of the unit tests I wrote for the core project after being run on windows[br][br]They also ran successfully on the Nintendo Switch but I can't show that because of my NDA

The output of the unit tests I wrote for the core project after being run on windows

They also ran successfully on the Nintendo Switch but I can't show that because of my NDA

I set up unit testing projects using googletest and made them run on the Nintendo Switch as well. I created unit tests for the core project, config module, and transform module. This helped the project a lot because these core parts of the codebase were depended on by every other part of the codebase, so verifying they work as expected prevented many potential issues. Several bugs were found during the writing of these tests that would have been difficult to debug and would have caused significant delays if they hadn't been found early.

Example of a unit test for the event system

This test made sure that a single event listener would receive the appropriate events. It also made sure the listener would stop receiving events when removed from the event system

Obviously, this wasn't the only test for the event system, there are also tests for all combinations of multiple events and multiple listeners

OpenGL Renderer

Screenshot of the OpenGL renderer[br][br]This shows that it can load and display models the same way the DirectX20 renderer from the graphics programmer did

Screenshot of the OpenGL renderer

This shows that it can load and display models the same way the DirectX20 renderer from the graphics programmer did

Because our project needed to support the Nintendo Switch but we only had one graphics programmer who had their hands full with a DirectX20 renderer for windows for most of the project (they did implement a native Switch renderer later on but we needed to test our other code on both platforms before that), I took on the task of writing a quick renderer with OpenGL so that we could see our game on both platforms.

This was very simple since the demo didn't even include light sources.

Nintendo Switch

I did the following things to allow our project to support the Nintendo Switch:

Jenkins Setup

Screenshot of our Jenkins dashboard at the end of the project[br]We're in the green!

Screenshot of our Jenkins dashboard at the end of the project
We're in the green!

Most of the work for the Jenkins setup was done by a teammate, but I helped create the build script for the Nintendo Switch builds.

There was an issue with file paths that were different on the build server machine than how we all set it up on our local machines, so I managed to fix the issue by adding some extra arguments to the build script that would change these paths.

Although the problem turned out to be caused by a simple command-line issue, I had to learn a lot about Jenkins to understand the setup and eventually find this problem.

Doxygen Setup

Screenshot of the Doxygen result showcasing the abstract base class of all modules

Screenshot of the Doxygen result showcasing the abstract base class of all modules

At the start of the project, I set up Doxygen so we could generate documentation for our codebase automatically based on our code comments.

This helped the project quite a bit, especially for teammates that needed to reference each other's work. I wrote extensive documentation for the core project since every other teammate depended on understanding how to work with it.

Simple batch file that runs Doxygen and then opens the documentation so that it would be updated whenever necessary

Profilers

Screenshot of Optick being used for Windows[br][br]I can't show the profiler I used for the Nintendo Switch because I signed an NDA

Screenshot of Optick being used for Windows

I can't show the profiler I used for the Nintendo Switch because I signed an NDA

I also set up profiling for the engine on both platforms. We were recommended the Optick profiler by teachers, so I set that one up for windows. I also set up a native profiler for the Nintendo Switch.

All calls to the profiler are done in preprocessor definitions so that they can be changed based on the platform or fully omitted when profiling is unnecessary.

The profiler being initialized and updated per frame in the game loop

The profiler being used by a module to mark its initialization stage and its portion of a game tick


Project tags: #C++, #Cross-Platform, #Custom-Engine, #ECS, #Graphics, #Jenkins, #Nintendo Switch, #OpenGL, #UML, #Unit Testing


Similar projects:

Reboot: Cross-Platform Modular ECS Game Engine Thumbnail

Reboot: Cross-Platform Modular ECS Game Engine

Keep reading

Cabinet of Curiosities Thumbnail

Cabinet of Curiosities

Keep reading

Native Graphics for Nintendo Switch Thumbnail

Native Graphics for Nintendo Switch

Keep reading

More projects...