Synopsis
Play as a smuggler cat stranded on an isolated island. An old ghostly man approaches you with an offer: help him reignite an ancient beacon, and he too shall help you. Fight off sword-wielding sharks and solve puzzles to make your way up the tower to fulfill your promise. Will the old man uphold his part of the deal?
Development
The Keeper's Light is a third-person action adventure game made by a team of students from The Game Assembly Malmö. This was my seventh project as a game programmer at TGA. The game runs on our own engine and render pipeline, along with our editor for runtime debugging and testing. My primary responsibility during this project was implementing and expanding a visual scripting system into our engine and editor.
Team: 16 people
Duration: 15 weeks (20h/week)
Engine: RockSolid (in-house)
Level Editor: Unreal
Team: 16 people
Duration: 15 weeks (20h/week)
Engine: RockSolid (in-house)
Level Editor: Unreal
Contributions
Engine
・ Integrated and further developed a visual scripting system from another course into our engine.
・ Created nodes for different components as needed, exposing their public interface to visual scripting.
・ Expanded the particle system with custom pixel shaders, velocity-aligned particles, and an option for fixed facing direction.
・ Added option for transparent UI images to blur everything behind them.
・ NavMeshAgent component moving along a path as a general guide rather than following it precisely.
・ Feather-weighted debug rendering of triangle mesh colliders.
・ Created nodes for different components as needed, exposing their public interface to visual scripting.
・ Expanded the particle system with custom pixel shaders, velocity-aligned particles, and an option for fixed facing direction.
・ Added option for transparent UI images to blur everything behind them.
・ NavMeshAgent component moving along a path as a general guide rather than following it precisely.
・ Feather-weighted debug rendering of triangle mesh colliders.
Editor
・ Integrated and further developed a visual script graph editor from another course into our editor, allowing other disciplines (level designers and technical artists) to create scripts with nodes and apply them to GameObjects.
・Implemented GameObject picking. Select a GameObject by clicking on it in the Viewport.
・Implemented GameObject picking. Select a GameObject by clicking on it in the Viewport.
Graphics
・ Improved emission and bloom.
・ Implemented lit volumetric fog through ray marching.
・ Shadow mapping for spot lights and point lights.
・ Implemented lit volumetric fog through ray marching.
・ Shadow mapping for spot lights and point lights.
Gameplay
・ Camera and character controls for controller and keyboard/mouse with three modes: normal, lock-on, and aiming.
Visual Scripting
We got the foundation of this system at the start of a course in scripting, and built upon it throughout the course up to a point where we could make a small game in TGE (a barebones C++ framework provided by the school) powered by scripts. It was then time for me to integrate the system and graph editor into RockSolid (our in-house engine and editor) to allow our level designers and technical artists to implement features themselves. That couldn't possibly be that much work, right?

WRONG! I could go on a rant about this, but that would be unprofessional. Instead, here's a bullet list of how the visual scripting system was integrated into RockSolid, along with some much-needed bug fixes and quality-of-life improvements.
・ Proper script assets that store script graphs without running the scripts.
・ Script component that references a script asset and uses it to create a script instance.
・ Ability to view and edit both script assets and instances of one.
・ Ability for script graphs, nodes, and pins to handle weak pointers to GameObjects and components.
・ Porting the script graph editor to work with out editor windows.
・ Complete rework of the variables menu to imitate the workflow in Unreal.
・ Nodes menu opens after dragging and letting go of an edge.
・ Contextual nodes menu, only showing nodes compatible with the pin.
・ Compatible nodes automatically connect.
・ Drag and drop scripts from Windows file explorer onto GameObjects or into the script graph editor window.
・ Total of 300+ nodes for primitive types, vectors, GameObjects, and some components, using a lethal dose of macros.
・ Pasted nodes appear at the center of your screen instead of exactly where they were copied.
・ Bizarre workarounds to allow dropdown menus and other popups from nodes (because why would that work as expected?).
・ Making sure it doesn't crash when deleting animated nodes.
・ Script component that references a script asset and uses it to create a script instance.
・ Ability to view and edit both script assets and instances of one.
・ Ability for script graphs, nodes, and pins to handle weak pointers to GameObjects and components.
・ Porting the script graph editor to work with out editor windows.
・ Complete rework of the variables menu to imitate the workflow in Unreal.
・ Nodes menu opens after dragging and letting go of an edge.
・ Contextual nodes menu, only showing nodes compatible with the pin.
・ Compatible nodes automatically connect.
・ Drag and drop scripts from Windows file explorer onto GameObjects or into the script graph editor window.
・ Total of 300+ nodes for primitive types, vectors, GameObjects, and some components, using a lethal dose of macros.
・ Pasted nodes appear at the center of your screen instead of exactly where they were copied.
・ Bizarre workarounds to allow dropdown menus and other popups from nodes (because why would that work as expected?).
・ Making sure it doesn't crash when deleting animated nodes.
Contextual search
As shown in the video to the right, letting go of an edge opens the nodes menu containing only nodes compatible with the starting pin.

300+ nodes
Some nodes are repetitive, and many of them are wrappers for existing public interfaces. For these types of nodes, I use macros to remove a lot of manual repetitive code.
With that said, the macros themselves took some time to set up.
With that said, the macros themselves took some time to set up.


Volumetric Fog
I approached volumetric fog out of pure curiosity in my free time, but once I started seeing promising results, I continued the work during project hours. I read a few papers and watched some YouTube videos to grasp concepts such as light scattering and transmittance. No source was fully translatable to our engine and render pipeline, thus, my implementation is a combination of multiple sources such as this paper from Siggraph 2014 and this video on YouTube.

Before/after volumetric fog

Settings
Improved emission and bloom
Our old method for handling bloom (which we used for Spite: Flesh and Flames) left a lot to be desired. We sampled the final LDR texture to create a luminance texture, which was then downsampled, blurred using Gaussian blur, upsampled, and then blended back into the LDR texture. It looked quite bad with high emission, so we kept the effect subtle.
After a bit of research, I came across a presentation from Sledgehammer Games (2014) explaining their approach to bloom in Call of Duty Advanced Warfare. They used multiple bilinear samples to downsample and blur the texture simultaneously, repeatedly down to the smallest possible mip level, before performing standard bilinear upsampling mip by mip with additive blending. I ended up using their method, but I used another method for upsampling found here.
After a bit of research, I came across a presentation from Sledgehammer Games (2014) explaining their approach to bloom in Call of Duty Advanced Warfare. They used multiple bilinear samples to downsample and blur the texture simultaneously, repeatedly down to the smallest possible mip level, before performing standard bilinear upsampling mip by mip with additive blending. I ended up using their method, but I used another method for upsampling found here.

Before/after improved emission and bloom

Before/after improved emission and bloom
Credits
Programming:
・ Erik Edfors
・ Filip Tripkovic
・ Herman Sjöholm
・ Liam Sjöholm
・ Måns Berggren
・ William Sigurdsson
・ Erik Edfors
・ Filip Tripkovic
・ Herman Sjöholm
・ Liam Sjöholm
・ Måns Berggren
・ William Sigurdsson
Graphics:
・ Albin Mjörnstedt
・ Emil Hagström
・ Jasper Paavolainen
・ Stephanie Madsen
・ Albin Mjörnstedt
・ Emil Hagström
・ Jasper Paavolainen
・ Stephanie Madsen
Animation:
・ Jesper Walden
・ Oskar Lind
・ Jesper Walden
・ Oskar Lind
Level Design:
・ Kristian Sistig
・ Martin Trosell
・ Kristian Sistig
・ Martin Trosell
Technical Art:
・ Elina Kans
・ Erik Hausner
・ Elina Kans
・ Erik Hausner