using System.Collections.Generic; using UnityEngine; using UnityEngine.Audio; namespace Hallucinate.Audio { public class AudioManager : MonoBehaviour { public static AudioManager Instance { get; private set; } [Header("Settings")] [SerializeField] private AudioDatabase database; [SerializeField] private int poolSize = 20; [SerializeField] private AudioMixerGroup defaultGroup; private List _pool; private int _currentIndex = 0; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); InitializePool(); if (database != null) database.Initialize(); } private void Start() { ApplyAllVolumes(); } private void InitializePool() { _pool = new List(); for (int i = 0; i < poolSize; i++) { GameObject go = new GameObject($"AudioSource_{i}"); go.transform.SetParent(transform); AudioSource source = go.AddComponent(); source.playOnAwake = false; _pool.Add(source); } } public void ApplyAllVolumes() { SetVolume("MasterVolume", PlayerPrefs.GetFloat("MasterVolume", 80f)); SetVolume("MusicVolume", PlayerPrefs.GetFloat("MusicVolume", 80f)); SetVolume("VFXVolume", PlayerPrefs.GetFloat("VFXVolume", 80f)); SetVolume("PlayerVolume", PlayerPrefs.GetFloat("PlayerVolume", 80f)); SetVolume("UIVolume", PlayerPrefs.GetFloat("UIVolume", 80f)); } public void SetVolume(string key, float volume) { if (defaultGroup == null || defaultGroup.audioMixer == null) return; // Chuyển đổi từ 0-100 sang dB (-80f đến 0f hoặc 20f tùy mixer) // Công thức: dB = 20 * log10(volume / 100) float db = volume <= 0.001f ? -80f : Mathf.Log10(volume / 100f) * 20f; // Đảm bảo Parameter đã được EXPOSE trong AudioMixer với tên tương ứng (MasterVolume, MusicVolume, etc.) defaultGroup.audioMixer.SetFloat(key, db); } public void Play(string sampleName, float volumeMult = 1f, float pitchMult = 1f, Vector3? position = null) { if (database == null) return; var sample = database.GetSample(sampleName); if (sample == null || sample.Clip == null) { // Silence or log warning if needed return; } AudioSource source = GetNextSource(); // Setup source source.clip = sample.Clip; source.volume = sample.DefaultVolume * volumeMult; source.pitch = sample.DefaultPitch * pitchMult; source.outputAudioMixerGroup = sample.MixerGroup != null ? sample.MixerGroup : defaultGroup; if (position.HasValue) { source.spatialBlend = 1f; // 3D source.transform.position = position.Value; } else { source.spatialBlend = 0f; // 2D } source.Play(); } private AudioSource GetNextSource() { // Simple round-robin for now, can be improved to find truly "idle" sources AudioSource source = _pool[_currentIndex]; _currentIndex = (_currentIndex + 1) % poolSize; return source; } public void PlayRandom(string baseName, int variants, float volumeMult = 1f, float pitchMult = 1f) { int rand = UnityEngine.Random.Range(1, variants + 1); Play($"{baseName}-{rand}", volumeMult, pitchMult); } // Helper for UI/Global easy access public static void PlayGlobal(string name, float volume = 1f, float pitch = 1f) { if (Instance != null) Instance.Play(name, volume, pitch); } public static void PlayRandomGlobal(string baseName, int variants, float volume = 1f, float pitch = 1f) { if (Instance != null) Instance.PlayRandom(baseName, variants, volume, pitch); } } }