// 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; [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; } } 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; src.clip = clip; src.Play(); indices[category] = idx + 1; } } }