Audio Manager

Hello everyone.

In my game, I let the user change the volume of audio.

I already have a volume slider working perfectly, but only for one sound (menu background music).

I want the sliders (I have one for BGM and one for SFX) to control all the sounds and music in the game.

Whats the best way to approach this?

Thanks!

Read this first ( 1 master volume in game )##

You can control the master volume via AudioListener.volume, and you can pause audio using AudioListener.pause.

However, if you want to have 2 master volume for BGM and SFX respectively, you will have to do the manual work ( in the segment after this one ).





For 2 or more master volume in game

Use a singleton AudioManager to control all the sound in your game. You need to add all existing AudioSource to the AudioManager in Start(), I will recommend using a dynamic collection to store the reference to the AudioSource, so you can add in sounds (SFX) that is generated in runtime.

From this singleton AudioManager, you will be able to control volumes.


Follow Up

Base File

Here’s is a skeleton for the audio manager. You use this by attaching this to a emtpyObject/camera. I am reusing sample codes from alucardj’s example.

public class AudioManager : MonoBehaviour
{
    private static AudioManager instance;
 
    void Awake() 
    {
       if (instance != null && instance != this) 
       {
         Destroy( this.gameObject );
         return;
       } 
       else 
       {
         instance = this;
       }
 
       //DontDestroyOnLoad( this.gameObject );
    }
 
    public static AudioManager GetInstance() 
    {
       return instance;
    }
}

The codes in the Awake() ensures that there will be only one AudioManager object in one scene. DontDestroyOnLoad() is used for this purpose, you can choose whether you want to keep it or not.

Master Volume

To achieve a master volume control, you need to add in a masterVolumeBGM in the AudioManager.

public class AudioManager : MonoBehaviour {
   public float masterVolumeBGM;

   ....
}

Then, in your GUI script, you will do something like this:

// Just call the instance as a class-variable, no need to assign
// the audioManager in the inspector
AudioManage am = AudioManager.GetInstance();    
float newVolume = GUILayout.HorizontalSlider (am.masterVolumeBGM , 0.0, 2.0);

if( newVolume != am.masterVolumeBGM ) { //The volume have changed
   am.masterVolumeBGM = newVolume;

   //Call a function to update audio's volume for BGM
}

Update Master Volume

In the AudioManager, you just need to use a simple loops to updates all the audioSource controlled by the AudioManager

public class AudioManager : MonoBehaviour {
   
   ...

   public void UpdateBGMVolume() {
      //Update volume of all audio source in scene
   }
}

Problems to tackle

However, this is not fully completed yet. You still have a few more problems to solve:

  1. All audio access: You need to search for all audioSource in the scene on Awake() and store their reference in a Collections, so you can control all of them later. Since you are using SFX, it is very likely that you will instantiate new audio in-game, you will need to use a dynamic collections. You can add the SFX to the AudioManager via something like this: AudioManager.GetInstance().Add( instantiatedAudioSource )
  2. Base Volume: You need to know the base volume of the audioSource to calculate the finalized volume. However, you cannot use audioSource.volume straight away, or else the calculation will become audioSource.volume = audioSource.volume * masterVolume; once you did this, the original volume is gone for good. You will need to store the starting volume of the audioSource.
  3. Dynamic Sound: When you use SFX, oftentimes, you will Destroy it when you are done. However, when a SFX is destroyed, the reference in audioManager will become null. You will need to periodically clean up the collections in your audioManager

Final Words

This might seems like a lot of work, but I can assure you that once you are done with it, you will have something very valuable, and you can reuse it over and over again in other game projects.