using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// GAME 1303 Tier 1 — One place to play music and sound effects.
///
/// SETUP
/// 1. Empty GameObject "AudioManager" with this script. It persists between scenes.
/// 2. Drop clips into the Named Sounds list and give each a name ("jump", "hit",
/// "collect", "death", "funny_death"). Optional: a Music clip that loops.
///
/// From anywhere:
/// AudioManager.Instance.PlaySFX("jump");
/// AudioManager.Instance.PlaySFX(myClip); // direct clip
/// AudioManager.Instance.PlaySFX("hit", 0.8f); // with volume
/// AudioManager.Instance.PlayMusic(bossTheme);
/// Also works from UnityEvents in the Inspector — pick AudioManager -> PlaySFX (string).
/// </summary>
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
[System.Serializable]
public class NamedSound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)] public float volume = 1f;
[Tooltip("Random pitch variation so repeated sounds don't feel robotic.")]
[Range(0f, 0.5f)] public float pitchVariation = 0.05f;
}
[Header("Sound Effects")]
public List<NamedSound> namedSounds = new List<NamedSound>();
[Tooltip("How many SFX can overlap.")]
public int sfxVoices = 8;
[Header("Music")]
public AudioClip musicOnStart;
[Range(0f, 1f)] public float musicVolume = 0.5f;
private AudioSource musicSource;
private AudioSource[] sfxSources;
private int nextVoice;
private Dictionary<string, NamedSound> lookup;
void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
DontDestroyOnLoad(gameObject);
musicSource = gameObject.AddComponent<AudioSource>();
musicSource.loop = true;
musicSource.playOnAwake = false;
sfxSources = new AudioSource[sfxVoices];
for (int i = 0; i < sfxVoices; i++)
{
sfxSources[i] = gameObject.AddComponent<AudioSource>();
sfxSources[i].playOnAwake = false;
}
lookup = new Dictionary<string, NamedSound>();
foreach (var s in namedSounds)
if (!string.IsNullOrEmpty(s.name) && !lookup.ContainsKey(s.name)) lookup[s.name] = s;
}
void Start()
{
if (musicOnStart != null) PlayMusic(musicOnStart);
}
// ---------- SFX ----------
public void PlaySFX(string name)
{
if (lookup != null && lookup.TryGetValue(name, out NamedSound s))
Play(s.clip, s.volume, s.pitchVariation);
else
Debug.LogWarning($"AudioManager: no sound named '{name}'.");
}
public void PlaySFX(string name, float volume)
{
if (lookup != null && lookup.TryGetValue(name, out NamedSound s))
Play(s.clip, volume, s.pitchVariation);
}
public void PlaySFX(AudioClip clip, float volume = 1f) => Play(clip, volume, 0.05f);
void Play(AudioClip clip, float volume, float pitchVar)
{
if (clip == null) return;
AudioSource src = sfxSources[nextVoice];
nextVoice = (nextVoice + 1) % sfxSources.Length;
src.pitch = 1f + Random.Range(-pitchVar, pitchVar);
src.PlayOneShot(clip, volume);
}
// ---------- Music ----------
public void PlayMusic(AudioClip clip)
{
if (clip == null || musicSource.clip == clip) return;
musicSource.clip = clip;
musicSource.volume = musicVolume;
musicSource.Play();
}
public void StopMusic() => musicSource.Stop();
public void SetMusicVolume(float v) { musicVolume = v; musicSource.volume = v; }
}