using Superlazy; using System.Collections.Generic; using UnityEngine; public class SLSound : SLGameComponent { private static GameObject soundRoot; private static bool mute = false; // 재생 관리를 위한 큐 private static readonly Dictionary> enableObjects = new Dictionary>(); // 페이드 private static readonly List fades = new List(); private static readonly List removes = new List(); // 리소스 풀 private static int unusedCount = 0; private static readonly Dictionary> unused = new Dictionary>(); // mute 정보 private static readonly HashSet muteTagList = new HashSet(); private static readonly List currentFramePlays = new List(); private static readonly Dictionary delaySounds = new Dictionary(); private static AudioSource GetAudioSource(string clipName) { if (currentFramePlays.Contains(clipName)) return null; currentFramePlays.Add(clipName); AudioSource audioSource = null; if (unused.ContainsKey(clipName) && unused[clipName].Count != 0) { audioSource = unused[clipName].Dequeue(); --unusedCount; } else { //TODO : 추후 사운드도 ResourceManager 로 이관해야함 var sound = SLResources.Load(clipName); if (sound != null) { sound.name = clipName; audioSource = soundRoot.AddComponent(); audioSource.clip = sound; } } return audioSource; } public static void MuteSounds(bool mute) { SLSound.mute = mute; } public static void ClearPool() { foreach (var queue in unused) { foreach (var source in queue.Value) { Object.Destroy(source); } } unused.Clear(); unusedCount = 0; } public static void PlaySound(string clipName, string tag = "Effect", bool loop = false, bool unique = false, bool reset = true, float volume = 1, float delay = 0) { if (string.IsNullOrEmpty(clipName)) return; if (clipName == "Off") return; // TODO: Off라는 이름의 사운드가 없어 임시 처리. 추후 수정 필요 if (soundRoot == null) return; if (tag == "BGM") { volume *= SLGame.Session["Option"]["Volume"]["BGM"] * 0.1f; } else { volume *= SLGame.Session["Option"]["Volume"]["SE"] * 0.1f; } if (delay > 0) { SLEntity context = SLEntity.Empty; context["Tage"] = tag; context["Loop"] = loop; context["Unique"] = unique; context["Reset"] = reset; context["Volume"] = volume; context["Delay"] = delay; delaySounds.Add(clipName, context); return; } var exist = false; if (unique) { if (enableObjects.ContainsKey(tag)) { var stopList = new List(); foreach (var source in enableObjects[tag]) { if (reset == false && source.clip.name == clipName) { exist = true; source.volume = volume; continue; } if (source.loop) { fades.Add(source); } else { source.Stop(); removes.Add(source); } stopList.Add(source); } foreach (var source in stopList) { enableObjects[tag].Remove(source); } } } if (exist) return; if (tag != "BGM" && tag != "Weather" && IsMuteTag(tag)) return; if (enableObjects.ContainsKey(tag) && tag != "TextSound") { if (enableObjects[tag].Count > 4) return; } var audioSource = GetAudioSource(clipName); if (audioSource != null) { // 여기서 각종 옵션을 제어한다. { audioSource.loop = loop; audioSource.volume = volume; audioSource.mute = IsMuteTag(tag); } audioSource.enabled = true; audioSource.Play(); if (enableObjects.ContainsKey(tag) == false) enableObjects[tag] = new List(); enableObjects[tag].Add(audioSource); } } public static void StopSound(string tag) { if (enableObjects.ContainsKey(tag)) { foreach (var source in enableObjects[tag]) { if (source.loop) { fades.Add(source); } else { source.Stop(); removes.Add(source); } } enableObjects[tag].Clear(); } } public static void SetMute(string tag, bool mute) { if (mute) muteTagList.Add(tag); else muteTagList.Remove(tag); } private static bool IsMuteTag(string tag) { if (mute) return true; if (tag == "BGM" || tag == "Weather") { return muteTagList.Contains("BGM"); } else { return muteTagList.Contains("Effect"); } } public static void VolumeChange(float before, float next) { foreach (var clip in enableObjects["BGM"]) { clip.volume = before == 0f ? next * 0.1f : clip.volume * next / before; } } public override void Begin() { if (soundRoot == null) { soundRoot = new GameObject("SoundRoot"); Object.DontDestroyOnLoad(soundRoot); } } public override void Update() { foreach (var sourceList in enableObjects) { foreach (var audioSource in sourceList.Value) { if (audioSource.clip.loadInBackground == false && audioSource.isPlaying == false) { removes.Add(audioSource); } else { audioSource.mute = IsMuteTag(sourceList.Key); } } sourceList.Value.RemoveAll(u => u.clip.loadInBackground == false && u.isPlaying == false); } foreach (var source in fades) { source.volume -= Time.unscaledDeltaTime; if (source.volume <= 0) { source.Stop(); removes.Add(source); } } fades.RemoveAll(u => u.volume <= 0); foreach (var audioSource in removes) { var name = audioSource.clip.name; if (unused.ContainsKey(name) == false) { unused[name] = new Queue(); } unused[name].Enqueue(audioSource); ++unusedCount; audioSource.enabled = false; } removes.Clear(); if (unusedCount >= 30) { ClearPool(); } if (delaySounds.Count > 0) { var removeDelaySounds = new List(); foreach (var delaySound in delaySounds) { delaySound.Value["Delay"] -= Time.unscaledDeltaTime; if (delaySound.Value["Delay"] <= 0) { var context = delaySound.Value; PlaySound(delaySound.Key, context["Tag"], context["Loop"], context["Unique"], context["Reset"], context["Volume"]); removeDelaySounds.Add(delaySound.Key); } } foreach (var key in removeDelaySounds) { delaySounds.Remove(key); } } currentFramePlays.Clear(); } public override void End() { } }