This is part one of a two part blog covering the sound design in Immortal Darkness. There are two sides to this story, one is technology, which this blog post is all about, and the other is the art. The tech that plays, mixes and manages the audio in the game is pretty cool, even if I do say so myself. But the art? The art is something else. All the music and ambient tracks in Immortal Darkness, have been created by Jan Glembotzki, an extremely talented composer and musician, based in Tokyo, Japan. In part 2 of this blog, I’ll be sharing an interview with Jan, and will talk about his influences and inspirations for the Immortal Darkness soundtrack. I stumbled on Jan’s work accidentally, while Googling for placeholder dungeon ambient sounds for our first demo, way back in April 2016. I dropped two of his pieces into our first prototype (Track 1 and Track 2), and we lived with them for a couple of months before I finally contacted Jan, with a long-shot offer to collaborate. To my surprise and glee, Jan was interested. I sent him some video of the game, and his enthusiasm grew even more. We got some paperwork squared away, and we started working on how we wanted the game to sound. I put together a document, roughing out the pieces we would need for the game, along with lists of adjectives to try and convey the mood I wanted.
The rest, as they say… will be covered in the second part of this blog 🙂
Right from the outset of development, my intention was to create a detailed, dynamic, rich and contextually compelling audio experience for the player. I wanted lots of variation and the sound and mood to change, depending on what was happening to the player in the game. To accomplish this goal, I knew I needed to create a smart audio manager, that could interpret what was happening in the game, and choose and play pieces of music and ambience to reflect what is going on.
The audio system in Immortal Darkness is fairly elaborate. I have several audio layers that are mixed and managed dynamically, based on what the player is doing and other, in-game, in-world cues and triggers. Tracks are cued, volumes are adjusted and layers will cross fade, forming a seamless soundscape that continually adjusts to what the player is doing.
Layer #1 is the “Ambient Base” layer. It fills the quiet moments where Shade is huddled and injured, and recouping from their battles. The ambient track gives a solid foundation or background, to play other effects and pieces against.
Layer #2 is the “Ambient Effects” layer. Distant crashes and impacts, moaning and chanting, rumbles and dripping water. These effects are played semi-randomly / algorithmically and mixed in with the base ambient layer, to reenforce the imnmersive ambience even further.
Layer #3 is the “Incidentals” layer and is used for reveals and “wonder moments”. What is a “wonder moment”, I hear you ask? Imagine the player walks out onto a ledge overlooking a dark ritual in a stone circle in progress, or a bottomless pit full of fire, and as the vista is revealed, a short piece of swelling or atmospheric music is triggered and played to reinforce the moment.
Layer #4 is the “Exploration” layer. As the player walks around and explores the dungeon, stumbling on devious traps and taking time to solve a puzzle, short pieces of music, each around 1 minute long, are chosen and played occasionally. The idea is to break up the ambient soundscape with something more melodic and musical, adding interest and color. The frequency of the exploration tracks is somewhat random, but these tracks can also be triggered by external events, like finishing a particularly tough battle.
Layer #5 is the “Danger” layer. As the player explores and comes into contact with enemies, they will notice a change in the ambience, a shift to a more menacing, edgy and tense track. The danger track’s volume will fade, based on proximity to the threats around the player. The proximity of the enemies represents a threat level to the player; the closer the enemies, the higher the threat, and is calculated by averaging the ranges of all enemies that are within a defined “threat distance” to the player. The value is then used to calculate a volume for the danger track, and then used again to calculate an inverse volume to apply to the ambient track, so as one fades up, the other fades down, and visa-versa, giving a smooth transition in sound and mood, as the player slips from threat to exploration to combat to threat again.
Layer #6 is the “Combat” layer, and plays whenever the player is in combat with 2 or more enemies at the same time. The combat track will play until all the enemies in the fight are killed, where it will fade away, wafting like the ashes of your enemies in the breeze.
Layer #7 is the “Environment” layer, and plays all the location specific ambient and cue sounds. Things like distant waterfalls on the Underground Sea level, heard when taking a raft to the far West of the level, or mournful wind sounds while crossing a bottomless pit on a moving floor. These sounds are controlled by in game trigger volumes and areas. As the player move in and out of them, environmental audio is triggered, faded, and managed.
All of these layers are managed by my Audio Manager. Any of the layers can be controlled externally, using hooks, allowing for even more applications. Sounds can be triggered using animation events for example. When it all comes together the sound in Immortal Darkness can be truly emotive, and gives a solid audio experience for the player. It all ends up working quite well.
Code wise, in Unity, I’m not doing anything particularly special. I’m using vanilla Unity Audio Listeners and Audio Sources, with my own logic and manager wrapped around it all. The clever stuff is in the logic and hooks into the game so the Audio Manager can make smart choices of what to play next (or right now). Unity Audio Sources are pretty flexible, offering spacial settings for 3D, positional audio, as well as a handful of other parameters you can tweak. Some of my layer audio sources require ongoing monitoring and maintenance over time, so using the AudioSource method Play(), allows me to adjust stuff like volume while playing, or stop a sound early. The exploration, danger, combat and ambient tracks use this method, so they can be adjusted dynamically. It gives me a persistent hook into the sound playing on that source. I have other layer Audio Sources, like the ambient effects layer, that are exclusively “one shot”, meaning I can play as many sounds on that source as I like, provided I don’t need to manage them over time, or stop them early. The only gotcha here is volume. You have to set the Audio Source volume to 1.0f (full volume) but pass the actual volume for the “one shot” sound, as a parameter. Otherwise, all sounds currently playing on the source will change in volume. That one got me scratching my head for a few hours.
All the audio assets in the game are collected and managed in a Google spreadsheet. Tokenized, symbolic names are defined in the doc, along with mixing volume levels and pitch, and the actual Unity asset name. The file is exported and parsed by the game to generate a library of available clips, with bindings to the symbolic names defined in the doc. Then I can just play sounds by name from anywhere in the game.
I’m working on Part 2 of this blog, which will be an interview with Jan Glembotzki, our super talented composer.