Skip to main content

How to make an Advanced Audio Manager for Unity - Technoob Technology

Unity engine gives us a good control on audio clips we use, But the problem is when we add several audio sources for several audio clips it's gonna get hard to control them all. And if you want to make some changes to only one audio source you have to find it first. 


audio-manager-technoob





This will become a mess if you gonna work with more than 10 audio clips.


In this case, we can Create an AudioManager and include all the audio clips in it and make some static methods to play, pause the audio from other scripts. This Audio Manager is written in C# and if you use unity-script you have to convert this into unity script first.


audio-manager-technoob


In this tutorial, we gonna create an audio manager which contains 7 methods.

1.Play
2.Pause
3.Unpause
4.Stop
5.FadeIn
6.FadeOut
7.lower volume for a duration


First, we need a Custom class which contains some strings, floats, bools and an audio clip. This class is not derived from unity mono behavior so we should add [System.Serializable] to make sure this class can be viewed in the Unity inspector.


using UnityEditor;
using UnityEngine;

[System.Serializable]
public class AudioFile
{

    public string audioName;

    public AudioClip audioClip;

    [Range(0f,1f)]
    public float volume;

    [HideInInspector]
    public AudioSource source;

    public bool isLooping;

    public bool playOnAwake;

}

We use the [HideInInspector] to hide the audio source component in the inspector as we not gonna use it through inspector.

After creating the AudioFile class we need to create the AudioManager script. This is where all the work is done. AudioManager should be derived from Monobehaviour. Add this script to an empty GameObject called AudioManager.
First, we have to make sure our audio manager is singleton which means that there should only be one audio manager in the scene.

Add this variable,

public static AudioManager instance;

Then in the awake method, we check if the instance is null and if it is null we set the current AudioManager to this and if there is another instance we gonna destroy the AudioManager. Don't think too much about this if it looks confusing. Just make sure to add these code lines in the awake method. "DontDestroyOnLoad" is a Unity method and it prevents destroying the Audio Manager when a new scene is loading.

if (instance == null)
{
instance = this;
}

else if (instance != this)
{
Destroy(gameObject);
}

DontDestroyOnLoad(gameObject);

We have to add the namespace "UnityEngine.Audio" to access all the functions to control the audio source. 

Then we need to create an array of AudioFile script we have written above. 

public AudioFile[] audioFiles;

After saving this you can see the AudioFile array in the inspector and we can set the size of the array and add an audio clip and set the options we need. The name is important as we gonna play the audio clips using the name in all the methods.


audio-manager-technoob


Then in the Awake method of our AudioManager, we gonna loop through all the AudioFiles in the AudioFile array and add an audio source per each AudioFile. Then we set the values of the AudioFile to the AudioSource we have added.

foreach (var s in audioFiles)
{
s.source =gameObject.AddComponent<AudioSource>();
s.source.clip = s.audioClip;
s.source.volume = s.volume;
s.source.loop = s.isLooping;
if(s.playOnAwake){
s.source.Play();
}
}

In this code we set the audio clip of the AudioFile to the AudioSource, then set the options we need and if the playOnAwake bool is true we play that audio clip. 

Now let's see the methods that we gonna create.




01)PlayMusic Method 

This method is used to play the audio clip which we want from the AudioFile array we have written above.

public static void PlayMusic(string name)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
return;
}
else
{
s.source.Play();
}
}

We need to pass the name of the AudioFile in order to find the audio clip from the array. These methods are static as we can call them from any other scripts. What this PlayMusic does is we find the AudioFile where its name equals to the name we pass, and if it matches then we call the play method of the audio source. If the name we passed doesn't match any AudioFile name then we just return with a Debug.LogError. 


02)StopMusic Method

This method is same as above instead we call the stop method of the audio source to stop the music. 

public static void StopMusic(String name)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
return;
}
else
{
s.source.Stop();
}
}

We pass the AudioFile name in all these methods to find the correct audio clip.

03)PauseMusic method

Same as above codes.

public static void PauseMusic(String name)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
return;
}
else
{
s.source.Pause();
}
}

04)UnPauseMusic method

Same as above

public static void UnPauseMusic(String name)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
return;
}
else
{
s.source.UnPause();
}
}

05) LowerVolume method

This method is a bit complicated than the above ones. This method can be used to lower the volume of an audio source for a given period of time. We need some variables and we have to use the update method to complete the LowerVolume method. This can be done in several other ways, But this would be the easiest way to understand the code and this works fine without any problem or error.

We need to declare some variables first.

private float timeToReset;
     private bool timerIsSet = false;
     private string tmpName;
     private float tmpVol;
     private bool isLowered = false;

Then the code lines would look like this.

public static void LowerVolume(String name, float _duration)
{
if (instance.isLowered == false)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
return;
}
else
{
instance.tmpName = name;
instance.tmpVol = s.volume;
instance.timeToReset = Time.time + _duration;
instance.timerIsSet = true;
s.source.volume = s.source.volume / 3;
}

instance.isLowered = true;
}
}

First, we check if this method is in use, if not we find the audio file with the name we pass into the method. Notice the float _duration which we pass into the method. Then we set the variables according to the AudioFile details. 

s.source.volume = s.source.volume / 3;

In this line of code, we set the volume of the audio source to 1/3 of the current volume, you can set it as you want or create a new parameter for this.

After these codes have executed we then write some code in the Update method to reset the volume after the given time.

private void Update()
{
if (Time.time >= timeToReset && timerIsSet)
{
ResetVol();
timerIsSet = false;
}
}


If the time is larger than or equal to the given time then we call the ResetVol method and it resets the volume according to the variables we set.

void ResetVol()
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == tmpName);
s.source.volume = tmpVol;
isLowered = false;
}



06)FadeIn method

We use a coroutine to make the fade-in effect. The static method FadeIn is used to call the IFadeIn coroutine. 
This is the static method which we use to call the coroutine.

public static void FadeIn(String name, float targetVolume, float duration)
{
instance.StartCoroutine(instance.IFadeIn(name, targetVolume, duration));
}


First, there should be some variables in order to control the coroutine.

private bool fadeIn = false;
     private string fadeInUsedString;

Then we check if the coroutine is being used by checking the bool fadeIn, if not first we set the audio source volume to 0 and then increase it within the given time. 

yield return new WaitForSeconds(duration);

is used to turn off the fadeIn bool after the fade in is done.


public IEnumerator IFadeIn(string name,float targetVolume, float duration)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
yield return null;
}
else
{
if (fadeIn == false)
{
fadeIn = true;
instance.fadeInUsedString = name;
s.source.volume = 0f;
s.source.Play();
while (s.source.volume < targetVolume)
{
s.source.volume += Time.deltaTime / duration;
yield return null;
}

yield return new WaitForSeconds(duration);
fadeIn = false;
}
else
{
Debug.Log("Could not handle two fade ins at once: " + name + " , " + fadeInUsedString+ "! Played the music " + name);
StopMusic(fadeInUsedString);
PlayMusic(name);
}
}
}


07).FadeOut method

This method is the same as the FadeIn method. 

private bool fadeOut = false;
private string fadeOutUsedString;

Static method 

public static void FadeOut(String name, float duration)
{
instance.StartCoroutine(instance.IFadeOut(name, duration));
}

Coroutine

private IEnumerator IFadeOut(String name, float duration)
{
AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);
if (s == null)
{
Debug.LogError("Sound name" + name + "not found!");
yield return null;
}
else
{
if (fadeOut == false)
{
fadeOut = true;
float startVol = s.source.volume;
fadeOutUsedString = name;
while (s.source.volume > 0)
{
s.source.volume -= startVol * Time.deltaTime / duration;
yield return null;
}

s.source.Stop();
yield return new WaitForSeconds(duration);
fadeOut = false;
}

else
{
Debug.Log("Could not handle two fade outs at once : " + name + " , " + fadeOutUsedString +"! Stopped the music " + name);
StopMusic(name);
}
}
}


In this method, we make sure to stop the Audiosource after the volume is lowered to the 0.



The final Scripts would look like this. You can clone the scripts from GitHub.


AudioFile


using UnityEditor;

using UnityEngine;


[System.Serializable]

public class AudioFile

{


    public string audioName;


    public AudioClip audioClip;



    [Range(0f,1f)]

    public float volume;


    [HideInInspector]

    public AudioSource source;


    public bool isLooping;


    public bool playOnAwake;


}


AudioManager


using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.Audio;


public class AudioManager : MonoBehaviour

{


  #region VARIABLES


  public static AudioManager instance;


  public AudioFile[] audioFiles;


  private float timeToReset;


  private bool timerIsSet = false;


  private string tmpName;


  private float tmpVol;


  private bool isLowered = false;


  private bool fadeOut = false;


  private bool fadeIn = false;


  private string fadeInUsedString;


  private string fadeOutUsedString;

 #endregion

 

 

 // Use this for initialization

 void Awake()

 {

 if (instance == null)

 {

 instance = this;

 }


  else if (instance != this)

 {

 Destroy(gameObject);

 }


  DontDestroyOnLoad(gameObject);


  foreach (var s in audioFiles)

 {

 s.source = gameObject.AddComponent<AudioSource>();

 s.source.clip = s.audioClip;

 s.source.volume = s.volume;

 s.source.loop = s.isLooping;

 if (s.playOnAwake)

 {

 s.source.Play();

 }

 }

 }



  #region METHODS


  public static void PlayMusic(string name)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 return;

 }

 else

 {

 s.source.Play();

 }

 }


  public static void StopMusic(String name)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 return;

 }

 else

 {

 s.source.Stop();

 }

 }


  public static void PauseMusic(String name)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 return;

 }

 else

 {

 s.source.Pause();

 }

 }


  public static void UnPauseMusic(String name)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 return;

 }

 else

 {

 s.source.UnPause();

 }

 }


  public static void LowerVolume(String name, float _duration)

 {

 if (instance.isLowered == false)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 return;

 }

 else

 {

 instance.tmpName = name;

 instance.tmpVol = s.volume;

 instance.timeToReset = Time.time + _duration;

 instance.timerIsSet = true;

 s.source.volume = s.source.volume / 3;

 }


  instance.isLowered = true;

 }

 }


  public static void FadeOut(String name, float duration)

 {

 instance.StartCoroutine(instance.IFadeOut(name, duration));

 }


  public static void FadeIn(String name, float targetVolume, float duration)

 {

 instance.StartCoroutine(instance.IFadeIn(name, targetVolume, duration));

 }




        //not for use

    private IEnumerator IFadeOut(String name, float duration)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 yield return null;

 }

 else

 {

 if (fadeOut == false)

 {

 fadeOut = true;

 float startVol = s.source.volume;

 fadeOutUsedString = name;

 while (s.source.volume > 0)

 {

 s.source.volume -= startVol * Time.deltaTime / duration;

 yield return null;

 }


  s.source.Stop();

 yield return new WaitForSeconds(duration);

 fadeOut = false;

 }


  else

 {

 Debug.Log("Could not handle two fade outs at once : " + name + " , " + fadeOutUsedString +"! Stopped the music " + name);

 StopMusic(name);

 }

 }

 }


  public IEnumerator IFadeIn(string name,float targetVolume, float duration)

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == name);

 if (s == null)

 {

 Debug.LogError("Sound name" + name + "not found!");

 yield return null;

 }

 else

 {

 if (fadeIn == false)

 {

 fadeIn = true;

 instance.fadeInUsedString = name;

 s.source.volume = 0f;

 s.source.Play();

 while (s.source.volume < targetVolume)

 {

 s.source.volume += Time.deltaTime / duration;

 yield return null;

 }

 

 yield return new WaitForSeconds(duration);

 fadeIn = false;

 }

 else

 {

 Debug.Log("Could not handle two fade ins at once: " + name + " , " + fadeInUsedString+ "! Played the music " + name);

 StopMusic(fadeInUsedString);

 PlayMusic(name);

 }

 }

 }


  void ResetVol()

 {

 AudioFile s = Array.Find(instance.audioFiles, AudioFile => AudioFile.audioName == tmpName);

 s.source.volume = tmpVol;

 isLowered = false;

 }


  private void Update()

 {

 if (Time.time >= timeToReset && timerIsSet)

 {

 ResetVol();

 timerIsSet = false;

 }

 }


  #endregion

}




CheckOut our popular articles




Comments

Popular posts from this blog

How to make a first person character controller - Unity

A first-person character controller script is the starting point of any fps shooter game. Even new Unity game developers tend to write their own fps controller script before learning anything else as it is challenging and very exciting. In this article, we gonna create two simple fps controllers from scratch and will guide you step by step on how it works. First, let's understand the basic concept of an fps controller. You should have a rotating camera that rotates in the x-axis to look up and down and the character should rotate on the y-axis to look around. There are two ways to create an fps controller in the Unity Engine.  Using Character controller component Using the RigidBody component Both have different advantages and disadvantages when using them. It depends on your game to select the best fps controller according to your needs.  Character controller based fps controller Pros: Is grounded check is built-in Has its own methods to move the chara

Unity Get Post web requests - Technoob Technology

Web requests in unity are super easy to use. We gonna use the UnityWebRequest class as the WWW class is deprecated now. UnityWebRequest class can be used for both Get and Post methods. The 'UnityEngine.Networking' namespace is required to work with UnityWebRequests.  This tutorial is made with some simple UI elements. First Let's see how the GET method works. 01. UnityWebRequest.GET We use this method to receive data from the server as normal get requests. Using coroutines in web requests is mandatory as we have to wait until the download is complete before showing the results, and the coroutines make this much easier. A simple button calls the method and a text element will show the result. The script contains a reference to the text element and the string URL of your PHP file.  When the button is pressed the button executes the ButtonGetData method and that method simply starts the GetData coroutine. We use the using keyword as this data