Audio Implementation - The Cure: Peculiar Pestilence

The Gist

In April 2022 I participated in the “Week Sauce” game jam with some GDC Conference Associate friends. The theme was, “Pick a musical group and turn their name into the theme of your game,” so we made a game about finding “The Cure” for various peculiar diseases. The player assumes the role of a plague doctor who must mix various ingredients to create potions and deduce which ones cure the patients’ ailments. We used Unity as our engine and FMOD as our audio middleware.

For this project, I took on the audio middleware integration, implementation, and troubleshooting. If you’d rather watch a gameplay video overview of the audio system than read a bunch of words about it, you can do that. Or you can watch the video and read a bunch of words about it. You can do anything you set your mind to. If, in particular, you’ve set your mind to implementing a quick-and-easy FMOD audio system in a Unity WebGL game in particular, read on.

Potion-mixing pandemonium

The Euphoria That Is Unity WebGL Audio

Unity WebGL audio is a pain in the ass.

  • Audio events triggered soon after your game loads can sometimes result in “event not found” errors.
  • Audio won’t auto-play once your game is loaded until the user explicitly interacts with it.
  • It’s single-threaded, which in itself is relatively limiting, and can lead to some frustrating issues, like stuttering audio buffer underruns if parts of your game get performance-intensive.

Luckily, you can fix the former two pretty easily!

  • WebGL/HTML5 requires asynchronous FMOD sound bank loading. So if your game at runtime hits some code telling it to load an FMOD sound event, before your sound banks are fully loaded, you’re gonna have a bad time. If you hear sound just fine in the Unity editor, but get silence and “event not found” errors in your web build, this is likely why. Here’s an easy-to-implement async loading script from the FMOD docs, and some important FMOD-on-WebGL considerations.

  • An easy workaround to the require-user-interaction-to-play-audio feature is to have your game initially load a barebones scene that’s empty aside from a “Start Game” button (or whatever fun little icon/object you want the player to interact with in order to run the game). The “Start” button can in turn load the proper Main Menu scene, which is then able to play your music and UI sounds. Bonus points: Combine this with the solution to the previous problem by disabling the “Start” button until the FMOD sound banks have finished loading.

One more thing about WebGL I have to mention: If you’re making a WebGL build in Unity, make sure you have the Unity WebGL plugin installed… It took me an embarrassingly long time to figure out this is why our StreamingAssets folder and sound banks were MIA.

Bite-Sized Options Menu Volume Sliders

In other news, here’s how I implemented some quick-and-easy volume sliders in our options menu:

Simple volume slider implementation

I made an AudioSettings class that holds references to the music and SFX buses (make sure you actually have both of these set up in the FMOD Studio project), and has functions to set the volume level for each of them. The options menu scene has some Unity UI sliders, whose “On Value Changed” functions simply call the AudioSettings member function that updates the corresponding bus volume. The code is super simple, but if you take this approach for your own project, make sure that, 1) your sound events are all routed to the appropriate buses in your FMOD Studio project, and 2) your UI sliders’ initial values match the initial value for the bus volumes in the AudioSettings class.

public class AudioSettings : MonoBehaviour
{
    FMOD.Studio.Bus MusicBus;
    FMOD.Studio.Bus SFXBus;
    float musicVolume = 0.8f;
    float sfxVolume = 0.8f;

    void Start()
    {
        MusicBus = FMODUnity.RuntimeManager.GetBus("bus:/MusicBus");
        SFXBus = FMODUnity.RuntimeManager.GetBus("bus:/SFXBus");
    }

    public void UpdateMusicVolume(float newVol)
    {
        MusicBus.setVolume(newVol);
    }

    public void UpdateSFXVolume(float newVol)
    {
        SFXBus.setVolume(newVol);
    }
}

Other Audio Implementation

In addition to AudioSettings, I made an AudioManager singleton class that gets notified by our GameState singleton class when audio-relevant game state changes (e.g. in-game day/night, the number of sick patients in triage, whether the player is in the triage room or back room making potions). Upon notification of changed state, the AudioManager class updates corresponding FMOD global parameters, which our ambience, SFX, and music events use to modulate layers, panning, and filters.

The code is pretty straightforward, but here’s a little snippet from the AudioManager showing how our FMOD “Room” parameter is updated, using an enum and switch statement:

enum ROOM
{
    TRIAGE,
    POTION
};

void Start()
{
    // ...
    
    GameState.Instance.OnCurrentRoomChanged += OnCurrentRoomChanged;

    // ...
}

private void OnCurrentRoomChanged(Room newRoomState)
{
    switch (newRoomState)
    {
        case Room.TriageRoom:
            FMODUnity.RuntimeManager.StudioSystem.setParameterByName("Room", (float)ROOM.TRIAGE, false);
            break;
        case Room.PotionRoom:
            FMODUnity.RuntimeManager.StudioSystem.setParameterByName("Room", (float)ROOM.POTION, false);
            break;
        default:
            break;
    }
}

Note that the “Room” parameter is defined as a continuous float value in FMOD, but the code always assigns it either 0 or 1. This is intentional. By using a continuous parameter rather than a discrete one, we can draw smooth automation curves with a slow seek speed for a smooth transition from room to room over the course of a second or so, rather than a sudden, jarring change in the audio.

Automation curve over the Room parameter in FMOD Studio

Appreciations

I had a blast working on this game. I’m grateful I got to be part of this team of incredibly creative and supportive people, and I’m grateful for the GDC CA program for providing me with the opportunity to meet them in the first place. I learned quite a bit about FMOD integration and troubleshooting, FMOD mixing, working in the Unity editor, and writing C# code. Special shout outs to Angela Geiss (Sound Design), Ian Riley (Music Implementation), Sydney Roberts (Music Composition), Will Edmisten (Technical Sound Design), and Patrick Moffett (Programming) for being nice and making nice things.

Thanks for reading. GLHF.

Play “The Cure: Peculiar Pestilence” on Itch.io