3D Action
Created by Team Somninauts and Team Soulstice in Unreal Engine 5
An Action Adventure, In a small Victorian town, terrifying nightmares manifest, attempting to shatter the Witchbal, a mystical barrier safeguarding the sleeping residents. As a courageous deity, you must battle through relentless waves of nightmarish creatures, defending the Witchball and protecting the town from falling into eternal darkness. Save their dreams before it's too late!
Unreal 5
Knight Light started development in September 2023
Contributed Develoment Time: September 2024 - December 2024
The last 4 months of development remade the game from the ground up with a majority of new contributors, with new world, models, animations, UI, and gameplay loop
The first thing I did upon joining Knight Light was implement the saving and loading functions so a user could have their choice of settings be saved as they enter different states of the game and when closing or reentering the game.
This is how I handled saving the audio settings for Knight Light.
Instead of saving the RTPCs for the volumes directly, I opted to save floating point variables that correspond to the RTPCs, so if the user mutes the sound, whenever they unmute it will go to whatever values they had set them to previously.
In the game instance, I store the values in an object that just houses all the floating points, as well as a boolean for whether the game is muted. First it checks whether the current save game is valid or not (creating a new save data object if not) and storing the values.
In loading, which is called at the launch of the application and whenever the audio settings menu is opened, it checks whether the save data is valid and sets the RTPCs accordingly. If there is save data, it checks whether the game is muted, setting the RTPCs to 0 if so, and to the floating point values if not.
If there is no save data, it will create a new save data and set the values to the default values set by the sound designers.
When opening the audio settings, whether or not the game is muted, the sliders are set to the correct positions depending on what the volumes were set to.
Each slider is made of a custom object consisting of the slider, a value with the state of the slider, and art assets on top of the slider. The value of the slider will update the RTPC for its designated bus as the player moves it around.
If the game was muted, the sliders will be greyed out to show that they have been muted.
Unmuted
Muted
As dedicated audio programmer, I had to implement the different sound effects and state changes that happen during the game. One such task was the player footstep sound. The sound designers wanted the sound to change when walking on different materials, such as the stone streets, grass, and water.
I accomplished this by adding notify events to the animation of the running cycle which do a ray cast to see the physical material of whatever the player is standing on.
I fire the line trace straight down, making sure to ignore the player model since that would get hit otherwise.
Next I checked if the object that was hit (what's being stepped on) has a physical material attached to it.
If it doesn't, it prints a message saying what that object is and that it doesn’t have a physical material set, so while debugging we'll then be able to add one.
Next we play the sound.
Since the majority of objects were of similar materials, I had the sound designers make the stone sound generic enough to be used as the default. The other sounds, for grass and water, play when those terrains are walked over.
There’s tons of different things the player could attack, and each of those things had a different sound that had to make when attacked.
I took two different approaches:
The first approach was similar to how the footsteps worked: With Physical Materials.
There were several objects made of stone and wood, and tons of tree and bush varients, so using physical materials I was able to have the appropriate sound play from the base class of the objects. Similar to the footsteps, if the object did not have a physical material applied, text would appear on screen saying that the hit object didn’t have one. Using a switch node and a select node I could have the corresponding sound play when hitting an object, and could have certain types, such as water, not make a sound, since in this project there wasn’t much place to hit water so the sound designer did not make a sound for it.
I made a system using a Switch node and a Select node that, based on the Physical Material, would play the corresponding sound when hitting an object.
The second approach was based off of indiviual object types. There were some objects, such as lampposts, that were repeated in the world or simply composed of multiple different materials, so in their respective blueprints I had a different call to handle this where it would play their own sound.
There was a unique instance in Knight Light where two enemy variants would use the same animations but would play different sounds.
What I did was in the parent class for that enemy I created variables for each specific sound and made a special notify event to use with an enum for all the different animation blueprints.
In the notify description I had the enum listed so anyone other than me making adjustments to the animations would be able to immediately tell how to call the specific sound from the animation blueprint itself.
If the player tabbed out of the game, we needed the game to mute and to pause. I achieved this by modifying the C++ class of the parent gamemode class and created a blueprint node that detects the window change. If the game is in a menu outside of the levels, the game would just mute when tabbed-out, then restore the values of the RTPCs
If the player was in the actual game, I needed to also pause the game, but I did not want to pull up the pause menu if the player was in a cutscene or a gameover screen, so I created two booleans in the game instance that would change on what state the game is in that would get checked before calling the pause menu
One way I detected the change into and out ofa cutscene was by adding an event to the sequencer of the cutscene that would both set the boolean and play the music for the cutscene.
//Sample Text
For debugging purposes, I included a hotkey that would only mute the music so things like ambience and sound effects could be isolated. I set up the nodes in such a way that it wouldn’t matter if the music was already set to 0 or not to avoid any bugs that would come about from muting, then going to the menus and manually changing the volume in the menu.
Another hotkey I made for debugging and for running my QA checks on the weekly build was a simple god-mode key. Pressing G would bring up text that says whether it was just enabled or disabled, and would prevent any damage from enemies.
As Quality Assurance director, I lead the weekly build QA checks with the other programmers to check the status of known bugs and to see if new bugs were introduced.
I had an excel sheet that stored every weekly check in a different tab so we could easily see how progress was being made. The check was sorted into different categories based on where they were in the build and what they were, such as: The main menu, the player character, enemies, pause menu, and the level itself.
The sheet’s columns were split into what the test was, whether it passed, how it was tested, who tested it, when it was marked off, and a column for comments or notes on what was tested, such as a visual bug happening for an unknown reason, so it can be looked into more closely later on.
The excel sheet had conditional formatting to make it easy to tell what tests passed and failed with just a quick glance.
The tests I had listed were specific so nothing would go overlooked, such as having separate checks for proper sfx and vfx for the different abilities the player has, as well as things such as testing the ui of different game states in different resolutions to see if there was any clipping or things going past the screen border.
Examples of checks for the level scene
Examples of checks for the Player Character