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. It taught me more about C++ as a build than as a language, and about how the compiler, the linker and the project configuration depend on each other. The diagram above is one Marcus drew 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, and the Windows port is still what I work on.
Optimization
A large part of the internship was profiling. I tried a few profilers before settling on Tracy and Intel VTune, and used them on both the game and the build.
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, which is the before and after in the profile above.
Builds were the other one. I set up precompiled headers for the projects in the solution, which took a full build from 512 seconds to 300, roughly 41% faster. Nobody ever sees a build time in the finished game, but it is the number you spend most of your day waiting on.
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 is a test image with a known value at every pixel, which is what 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 left a debug primitive I could reuse anywhere, and it mostly ended up showing which way objects were facing and how they were rotated, which is the shot above of it running in the game.
Asset Packer
Towards the end of the internship I started on an asset packer, meant to cut the game's startup time. Instead of processing the source assets every time the game starts, they would be processed once by a separate command line program and stored 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.
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.
Recursive Asset Processing
The packer walks that tree. A folder means create the matching directory and carry on into its children, 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.
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"
},
"029 rubble_256x128.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/029 rubble_256x128.icon.png"
},
"031 sacrificial_altar.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/031 sacrificial_altar.icon.png"
},
"315 skull.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/315 skull.icon.png"
},
"317 moon.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/317 moon.icon.png"
},
"323 irma_mist1.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/323 irma_mist1.icon.png"
},
"325 irma_mist2.icon.png": {
"cmd": "copyfile",
"source": "biomes/biome_base_ext/base_ext.spritesheet.icons/325 irma_mist2.icon.png"
}
}
}
}
}
}
Processing Files
Each file is processed by the command named next to it in the project file, so a new kind of asset means adding a command rather than changing the packer.
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!
What I took away
This was the first codebase I worked in that I had not written, and it was not a normal 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. Build times, a port, debug tools, a packer. None of it shows up in the game, and it is the part I was kept on to keep doing. Someone has to keep the thing buildable on both platforms, and that turned out to be work worth being good at.
I also learned to measure before changing anything. Both of the numbers above came from a profiler telling me where the time actually went, and neither of them was where I would have guessed.