// Assets/_Project/Scripts/Audio/AudioManager.cs using System.Collections.Generic; using UnityEngine; namespace TD.Audio { public class AudioManager : MonoBehaviour { [System.Serializable] public struct CategoryConfig { public AudioCategory category; public int maxVoices; // NOTE: currently inert. It seeds each pooled AudioSource at init, but every // Play() call overwrites src.volume with (per-sound volume × SFX slider), so // this value never reaches the mix. Left as-is rather than "fixed" because // multiplying it in would halve every existing sound (authored scenes use 0.5). // Wire it up only alongside a pass to re-level the per-sound volumes. [Range(0f, 1f)] public float volume; } public static AudioManager Instance { get; private set; } [SerializeField] private CategoryConfig[] categories; private Dictionary pools; private Dictionary indices; private void Awake() { if (Instance != null) { Destroy(gameObject); return; } Instance = this; InitializePools(); } private void OnDestroy() { if (Instance == this) Instance = null; } private void InitializePools() { pools = new Dictionary(); indices = new Dictionary(); foreach (var config in categories) { var pool = new AudioSource[config.maxVoices]; for (int i = 0; i < config.maxVoices; i++) { var src = gameObject.AddComponent(); src.playOnAwake = false; src.volume = config.volume; pool[i] = src; } pools[config.category] = pool; indices[config.category] = 0; } } /// /// Plays a one-shot on the given category's voice pool. is /// the per-sound authored level; it's scaled by the player's SFX slider /// () on the way in. /// /// /// The scalar is sampled at play time, so a sound already in flight keeps the volume /// it started with when the slider moves. Every SFX here is short, so that's /// imperceptible and avoids having to track live voices. /// public void Play(AudioClip clip, AudioCategory category, float pitch = 1f, float volume = 1f) { if (clip == null) return; if (!pools.TryGetValue(category, out var pool)) return; int idx = indices[category]; var src = pool[idx % pool.Length]; src.pitch = pitch; src.volume = volume * AudioVolumeSettings.SfxScalar; src.clip = clip; src.Play(); indices[category] = idx + 1; } } }