Experimenting with Environmental Audio
I’ve recently started adding environmental sounds to Arc Vector, specifically wind and water. This adds a huge amount to the sense of immersion in a game world, especially if you don’t have continuous music filling in the space between gun fire and explosions.
It did, however, present some questions about implementation. Most game engines, like Unity - with which Arc Vector is being developed, allow easy positioning and playback of sounds, but where, exactly, does the sound of the ocean come from? Unity’s audio sources are represented by a point in 3d space, so how do we create a sound that should be audible throughout a large volume of space but not everywhere at the same volume? There are also areas of the map further inland where the sound of the sea should be reduced, what’s the best way to engineer that? Scattering hundreds or thousands of sound emitters across the map seemed an inefficient way of doing things, and I also wanted to avoid any additional, complicated pre-processing that would generate even more data that needed to be stored somewhere.
The approach I’ve been experimenting with is by no means the only way, or necessarily the correct way, or the best way, but as I like experimenting with things like this, I’m pleased that the solution I’ve tried gives good results.
Maps in Arc Vector are typically made up of a scattering of islands of different shapes and sizes, an archipelago. Now, we know most of the sound the sea makes is waves crashing on the beach, but I wanted my water to have an audio presence away from the coastlines as well, that sound of water gently lapping over itself, swirling, gurgling. It took some time to find a suitable source for this sound (I’ll leave that to your imagination!), sample it and cut out snippets that didn’t sound right, EQ it a little and eventually produce a reasonable length piece that looped cleanly. (Too short a piece and you’ll notice the loop, I’m typically using loops greater than 30 seconds to avoid this).
I’d established that I generally required a uniform volume for this sound across the map, but that the volume should fade out as you gain altitude and as you move inland. I realised that I could simply create a looping audio source at sea level and move its position around on the map to match the camera position, but not change its height. That way the volume would still be defined by the vertical distance of the camera to the water and the audio volume rolloff settings.
This worked great over open water but felt very strange when over low-lying terrain inland, hearing the water sloshing around beneath it. Initially I thought scaling the volume by a quick terrain altitude test might solve that, i.e. the more ground between you and the water the quieter, but it didn’t make sense for that low terrain or for areas near cliffs where the ground might be higher up, but you still expect to hear the sea.
What was needed was some sort of distance check to the sea. Initially I thought about scanning in each of the cardinal or diagonal directions until hitting water, but this seemed like it would require quite a lot of tests and could easily misread the map, even with geographical features as chunky as mine. Another idea I had was to sample the local area to establish how wet it was, or rather how much of it was under water. This might also have the advantage of giving a better idea of how loud the water should be at this point than a distance check. There might not be much water at the distance measured.
The solution I implemented tested a grid of just 5 x 5 points around the camera (the listening object, not the player ship) spread over an area of 512x512m. (I even spread these tests out over time a little by using a coroutine and yielding in the loop). It simply measures how many of those points are below water and uses that to scale the volume of the audio. After some experimentation I decided that treating 0% - 50% water coverage as 0% - 100% volume seemed to feel more natural, having the volume dip as you approached beaches and cliffs from the sea felt wrong, but reducing as you started to move inland felt right.
Remember this is used for the sound of deep water, not the sound of waves hitting the coast. To decide the volume of coastal wave audio, we want to analyse our tests slightly differently. If there are no points below water, we’re over land by (in this case) at least 256m (well, approximately!) Conversely if all terrain heights tested are below water, we’re out to sea instead, so in either case we want to calculate the sound of waves crashing to be zero, and at a maximum when the ratio is 50:50. You could easily introduce some weighting here based on the gradient of the test points, and maybe even use different audio samples for beach waves and rocky cliff waves.
So far, all good, and the volume of water seemed to change nicely as I flew over land and sea, and inland waterways, etc, but one thing I realised that wasn’t quite right, is that the sound was still centred on the stereo spectrum, as I was moving the audio source with the camera. I then realised that I was scanning my terrain in a typical fashion from a graphics point of view, x loop inside z loop, left to right from the top row to the bottom row. Although the order doesn’t really matter, it just affects how you add up, if you think about it as scanning columns from left to right it’s easier to see how you could then calculate a left/right weighting and set the stereo accordingly. I haven’t implemented this yet (I’ve spent too much time writing this article!) but it should be a pretty simple addition.
Splishy-splashy stuff done with, I’ve also been playing with wind effects. Again, I think many games would simply scale up the wind as you ascended a mountain, or entered a trigger zone, but I didn’t want to spend time scattering 100s of objects across the map and I wanted similar automatic scaling depending on altitude.
As I’d already created an object that matches the camera position at a height of zero and added a child object to that at the height of the water holding the water’s audio source, it was as simple as add another child with an audio source at a height of 120m. This is the height at which the wind sounds loudest, playing with this and with the volume falloff and radius allows easy balancing of the wind’s volume. I currently have it set so that it’s a bit lower as you get close to the water height, at it’s loudest near the cloud level, and fading to nothing as you go higher (I also fade out the sound of the player ship’s engine and the white-noise rush of the contrails at altitude to give the impression of a thin atmosphere).
Arc Vector has a fixed camera direction, so I haven’t worried about the stereo position of the wind audio yet and the source is a stereo file with slightly different, undulating left and right tracks, so I don’t really need to. However, as we know from experience if we turn our head the sound of the wind is very directional, so if you can rotate your camera view in your game, you’ll want your wind to sound right depending which way you’re looking.
One way you could do this is to have multiple audio sources spaced around, or more simply offset the position of the audio source to be slightly upwind, assuming your game engine handles stereo position nicely. Then it should be louder in the ear facing the wind, and balanced if facing into or downwind.
For most game environments you probably want to keep these types of audio quite low. It might seem like a waste of effort working on sound that’s barely audible but, even if it’s really quiet, experiment with turning it on and off to see how much it adds. Sometimes the best way to add atmosphere to a scene isn’t to drown everything out with music, but just to add the sound of the atmosphere itself.
Feel free to drop me any questions, or share any other tips on implementing audio in games either in the comments or on social media,