Audio Manager
Complete audio manager with music crossfade, SFX pooling, volume control, and persistence.
How to Use
1
Create an empty GameObject named 'AudioManager'
2
Attach this script
3
Add two AudioSource components for music (A and B)
4
Call AudioManager.Instance.PlayMusic(clip) for music
5
Call AudioManager.Instance.PlaySFX(clip) for sound effects
Source Code
C#
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
[Header("Audio Sources")]
[SerializeField] private AudioSource musicSourceA;
[SerializeField] private AudioSource musicSourceB;
[SerializeField] private int sfxPoolSize = 10;
[Header("Settings")]
[SerializeField] private float musicVolume = 0.7f;
[SerializeField] private float sfxVolume = 1f;
[SerializeField] private float crossfadeDuration = 1.5f;
private List<AudioSource> sfxPool;
private bool isMusicA = true;
private Coroutine crossfadeRoutine;
private AudioSource ActiveMusic => isMusicA ? musicSourceA : musicSourceB;
private AudioSource InactiveMusic => isMusicA ? musicSourceB : musicSourceA;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeSFXPool();
}
else
{
Destroy(gameObject);
}
}
private void InitializeSFXPool()
{
sfxPool = new List<AudioSource>();
for (int i = 0; i < sfxPoolSize; i++)
{
var source = gameObject.AddComponent<AudioSource>();
source.playOnAwake = false;
sfxPool.Add(source);
}
}
public void PlayMusic(AudioClip clip, bool crossfade = true)
{
if (clip == null) return;
if (crossfade && ActiveMusic.isPlaying)
{
if (crossfadeRoutine != null)
StopCoroutine(crossfadeRoutine);
crossfadeRoutine = StartCoroutine(CrossfadeMusic(clip));
}
else
{
ActiveMusic.clip = clip;
ActiveMusic.volume = musicVolume;
ActiveMusic.loop = true;
ActiveMusic.Play();
}
}
private IEnumerator CrossfadeMusic(AudioClip newClip)
{
InactiveMusic.clip = newClip;
InactiveMusic.volume = 0f;
InactiveMusic.loop = true;
InactiveMusic.Play();
float elapsed = 0f;
while (elapsed < crossfadeDuration)
{
elapsed += Time.unscaledDeltaTime;
float t = elapsed / crossfadeDuration;
ActiveMusic.volume = Mathf.Lerp(musicVolume, 0f, t);
InactiveMusic.volume = Mathf.Lerp(0f, musicVolume, t);
yield return null;
}
ActiveMusic.Stop();
isMusicA = !isMusicA;
}
public void PlaySFX(AudioClip clip, float volumeScale = 1f)
{
if (clip == null) return;
AudioSource source = GetAvailableSFXSource();
source.clip = clip;
source.volume = sfxVolume * volumeScale;
source.pitch = Random.Range(0.95f, 1.05f);
source.Play();
}
private AudioSource GetAvailableSFXSource()
{
foreach (var source in sfxPool)
{
if (!source.isPlaying)
return source;
}
// All busy — reuse the first one
return sfxPool[0];
}
public void SetMusicVolume(float volume)
{
musicVolume = Mathf.Clamp01(volume);
ActiveMusic.volume = musicVolume;
}
public void SetSFXVolume(float volume)
{
sfxVolume = Mathf.Clamp01(volume);
}
public void StopMusic(float fadeOutDuration = 0.5f)
{
StartCoroutine(FadeOutMusic(fadeOutDuration));
}
private IEnumerator FadeOutMusic(float duration)
{
float startVolume = ActiveMusic.volume;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
ActiveMusic.volume = Mathf.Lerp(startVolume, 0f, elapsed / duration);
yield return null;
}
ActiveMusic.Stop();
}
}