← Work
Case study · 2024 · Intern, then freelance

Trolldom Games

My 40-week internship at the end of my studies at Forsbergs. The game is Skoggangr, and it runs on an engine my mentor, Marcus Zetterquist, wrote from scratch in C++. I built debug tools and small gameplay features, profiled and cut build and launch times, and kept the Windows version in step with the Mac one. After the internship I stayed on as a freelancer on the Windows port until July 2026.

Porting to Windows

Skoggangr is made on Mac, and the Windows version had fallen behind. Bringing it back up to date was one of the first things I was given, and keeping it there meant porting changes across between the two platforms for the rest of the internship.

Most of that was Visual Studio project settings, adding and removing files from the solution, and the parts of the code that only exist on one platform, which meant working out how the compiler, the linker and the project settings fit together. Marcus drew a diagram to explain how Visual Studio turns the source files into a running game, and I went back to it every time a link error made no sense.

After the internship I stayed on as a freelancer, still on the Windows port, until July 2026.

Optimization

A large part of the internship was profiling. I tried a few profilers before settling on Tracy and Intel VTune.

The clearest find was at startup. Sound files were being loaded one after another on the same thread as everything else. Moving them onto a thread of their own took asset loading from 2.4 seconds to 1.6, about a third off.

I also set up precompiled headers for the projects in the solution, which took a full build from 512 seconds to 300, about 41% faster.

Debugging tools

I also made a few tools for the engine. The first generates a gradient pixelmap, looping over the pixels and giving each one a colour from its position. It's a test image with a known value at every pixel, which made it useful for checking that screenshots and rendering came out right.

The other was arrows. An arrow is a line plus a head, drawn from a start point, an end point and a head angle. The line direction is converted to polar coordinates to get its angle, the head angles are that angle plus and minus the head angle, and converting those back to vectors gives the two points the head is drawn to. That gave me a debug primitive I could reuse anywhere, and it mostly ended up showing which way objects were facing and how they were rotated.

Marcus also gave me the inspector to rework. It's the panel that holds all the debug tools and shows what's happening in the game. The old one was messy and didn't show all of the new features. The inspector is built with ImGui, so I had to learn ImGui for the rework. I also made a blood splatter and a screen shake effect that can be triggered from it.

Asset packer

Towards the end of the internship I started on an asset packer, meant to cut the game's startup time. The game processed its source assets every time it started. With the packer, a separate command line program would process them once and store them in a packed format the game could load directly.

I built the command line interface and its two subcommands, icons and updatepak. The icons work itself was Marcus's, and what I added was the command that made it reachable from the tool.

Command line tool
C:\Github_Repos\Game2\Win\Game2\x64\Release>asset_packer
No subcommand provided
Usage: asset_packer [OPTIONS] SUBCOMMAND

Subcommands:
  icons [dist_path] [output_path]    Build spritesheet icons
  updatepak [pak_file] [pak_path] [source_path]    Update pak folder

C:\Github_Repos\Game2\Win\Game2\x64\Release>asset_packer updatepak C:\Test 1\game2_pro...
Updating Pak Folder
Success!

The updatepak command takes a project file describing which assets to pack, how to process each one, and where the pak file goes. The project file is the asset structure written out as a tree of folders and files, with the command to run on each file named next to it. Adding a new kind of asset means adding a new command.

Example of project file
{
  "root": {
    "biomes": {
      "biome_base_ext": {
        "base_ext.spritesheet.icons": {
          "014 rune_stone1.icon.png": {
            "cmd": "copyfile",
            "source": "biomes/biome_base_ext/base_ext.spritesheet.icons/014 rune_stone1.icon.png"
          },
          "315 skull.icon.png": {
            "cmd": "copyfile",
            "source": "biomes/biome_base_ext/base_ext.spritesheet.icons/315 skull.icon.png"
          }
        }
      }
    }
  }
}

The packer walks that tree. A folder means create the matching directory and carry on into its children, and a file means run the command named on it. Copying was the main operation at the time, and I was working on adding ETC2 compression for PNG assets when the internship ended.

What I took away

This was the first codebase I worked in that I hadn't written, and it wasn't a typical one. There was no Unity or Unreal underneath it. One person had written the engine and the game in C++ over years, and the only way to change anything was to read enough of it to know what I would break.

Most of what I did was maintenance: the Windows port and the build times. None of it shows up in the game, and it's the part I was kept on to keep doing.