Academic / Group
C++, Azure, Premake, OpenGL
Co-Producer, Engine Dev, Gameplay Dev
Published on Steam
08/2019 - 05/2020
Arc Apellago is a 2D action platformer revolving with a focus on fast movements. I was involved in the project from its inception until the release on Steam. The team comprised of 5 programmers, 4 artists, 1 game designer, and 1 audio designer.
Please click on the colored heading below for details.
I contributed heavily as both an engine and a gameplay programmer. Please click the colored heading below for details.
Sorcerer character AI
I created the sorcerer character along with its AI. I designed, supported, and playtested the art pipeline. I designed a finite state machine and coded the monster.
I also added a transform parenting to the existing transform code. This allowed me to proceed with coding the sorcerer's projectiles revolving around the sorcerer.
In addition to the above, the sorcerer has three different attacks to code. I separated each of them into their own separate states in the finite state machine to handle.
A problem we hit early on in-game testing was that testers did not know how to play the game; despite a screen showing players the controls. I decided to add a quick in-game tutorial to solve this.
I added this tutorial by flashing the only two controls the player needs to know over the player's head. I used coroutines to achieve this.
Graphical User Interface
My work also involved fulfilling my artist's vision for the User Interface. In particular, I coded the UI for the Main Menu, the Pause Menu, and the Options Menu.
In addition to my primary role as Engine Dev, I also worked as the co-producer. My main work was to scope tasks and create reasonable timelines for the milestone.
I actively collaborated and aligned timelines for all disciplines involved to bring an idea to the screen as quickly and thoroughly as possible. Beyond just the tasking, I held monthly one-on-ones with each programmer to ensure their needs were all fulfilled.
I used Airtable to track tasks. I organized tasks by priority, which is in turn determined by how many tasks are blocked by it and how many unknowns are within the task. Below is a snapshot of the Airtable at the end of milestone two.
C++ Engine GUI Automation
I integrated a C++ real-time reflection library, RTTR, into the project. Since our engine was written in an Entity-Component-System concept, I was able to generate ImGUI defaults for each Component using RTTR.
The result is that programmers did not have to expend extra resources writing GUI code for our designers. Instead, a programmer only has to register their script, and the GUI is generated for them.
If a programmer wanted a custom GUI for their Component object, they could write tag their Object, and RTTR will attempt to use the programmer's own ImGUI functions.
I designed this code after researching and experimenting with what works in RTTR. In essence, I have to traverse each variant's property until I hit a C++ Basic Type, in which case I can generate the ImGui for it.
There are a couple of caveats to take note of. First, I had to handle containers uniquely, and I also had to handle user-defined classes to modify the correct instance of the class. Second, I could not display C-style strings as they are pointers.
Below is a diagram describing the general overview of how it works and a code snippet of the transformation from Basic Type to ImGUI.
C++ Objects JSON Serialization for Save/Load
Similarly to generating engine GUI, I used RTTR to facilitate the loading and saving of objects into JSON files. The game would load these data files. Thus the designer could modify the data in the engine using the ImGui editor and have their work saved for next time.
I wrote a blog on doing this on my friend's blog site that you can visit here. I also have a GitHub repository that accompanies the blog here. Below are the sample data structures I transformed to JSON.
I wrote a C++ Logger at the start of the project. There were a couple of considerations I had in mind when I wrote this logger.
Easy to use
Easy to invoke
Easy to attach any type of object in (like std::cout)
Customizable handling level (DEBUG, WARNING, ERROR, etc)
Saves the log to a file
Even if the program crashes
Displays log on console
Using it is as simple as calling
LOG(n_Logger::TRACE) << "Rainier Initialising" << n_Logger::endl;
As observable, the logger gives the file and the function signature of the caller. Programmers can extend the capabilities the same way they extend std::cout, by overloading an operator<<(std::iostream). I sent the stream message to both the console and C++'s I/O stream, thus logging to file as well.
In order to log even after a fail, I had to use Window's API SetUnhandledExceptionFilter(). This is a Window's only function, and I did not set one for Linux.
In the middle of development, I observed that programmers would occasionally push broken code to the source control. As we were in the middle of development, I judged that it would be too expensive to move to a new code production style. Hence I instead opted to automate compiling the project on every push, and inform the user when it fails.
An additional problem it can solve is keeping build artifacts. The production pipeline had no concept of experimental builds or stable builds; we just had builds. Thus using Azure to keep a record of builds was a great solution for our team.
Transitioning to an Azure build was also not difficult. I just had to learn how YAML files worked. The project itself already had git submodules and premake batch files setup.
In a span of 6 weeks with 12 programmers, it caught over 20+ bugs that break the code.