/* * @Author: 白哉 * @Description: * @Date: 2025年05月22日 星期四 14:05:32 * @Modify: */ using System.Collections.Generic; using Logic.CrashSight; using RuntimeData; using UnityEngine; namespace Logic.Audio { public class AudioManager { public static AudioManager Instance = new AudioManager(); private AudioPlayer _musicPlayer; private List _allPlayer; private Dictionary> _clips; private GameObject AudioRoot; public float MusicVolume { get { return _musicVolume; } set { _musicVolume = value; } } public float AudioVolume { get { return _audioVolume; } set { _audioVolume = value; } } private float _musicVolume = 1; private float _audioVolume = 1; public void Init() { _allPlayer = new List(); _clips = new Dictionary>(); var path = new Dictionary(); path["Main"] = "Audio/Main"; path["RemiliaEgyptian"] = "Audio/RemiliaEgyptian"; path["SatoriIndian"] = "Audio/SatoriIndian"; path["KanakoGermany"] = "Audio/KanakoGermany"; path["KaguyaFrench"] = "Audio/KaguyaFrench"; path["SFX/UI_buttonHover"] = "Audio/SFX/UI_buttonHover"; path["SFX/UI_buttonClick"] = "Audio/SFX/UI_buttonClick"; path["SFX/UNIT_click"] = "Audio/SFX/UNIT_click"; path["SFX/UNIT_bomb"] = "Audio/SFX/UNIT_bomb"; path["SFX/UNIT_archer"] = "Audio/SFX/UNIT_archer"; path["SFX/UNIT_hurt"] = "Audio/SFX/UNIT_hurt"; path["SFX/UNIT_move"] = "Audio/SFX/UNIT_move"; path["SFX/UNIT_attack"] = "Audio/SFX/UNIT_attack"; path["SFX/ENV_sea"] = "Audio/SFX/ENV_sea"; path["SFX/ENV_forest"] = "Audio/SFX/ENV_forest"; foreach (var kv in path) { _clips[kv.Key] = new List(); _clips[kv.Key].Add(Resources.Load(kv.Value)); } if (!AudioRoot) AudioRoot = new GameObject(); AudioRoot.name = "AudioRoot"; var cfg = Object.FindObjectOfType(); if (cfg != null) { foreach (var clip in cfg.Clips) { _clips[clip.name] = new List(); _clips[clip.name].Add(clip); } } } public void Update() { foreach (var player in _allPlayer) { player.Update(); if (player.State != PlayerState.Finished) continue; if (!player.Clip) continue; if (!_clips.ContainsKey(player.MusicName)) _clips[player.MusicName] = new List(); _clips[player.MusicName].Add(player.Clip); player.Clip = null; } } public void PlayMusic(string musicName, float fadeIn, float fadeOut, bool isLoop) { if (!_clips.ContainsKey(musicName) || _clips[musicName].Count == 0) return; if (_musicPlayer != null) _musicPlayer.Stop(); else _musicPlayer = GetPlayer(); if (_clips[musicName].Count > 1) { _musicPlayer.Clip = _clips[musicName][1]; _clips[musicName].RemoveAt(1); } else _musicPlayer.Clip = Object.Instantiate(_clips[musicName][0]); if (!_musicPlayer.Clip) { LogSystem.LogError($"音乐资源 {musicName} 未找到或加载失败!"); return; } _musicPlayer.MusicName = musicName; _musicPlayer.IsLoop = isLoop; _musicPlayer.Length = _musicPlayer.Clip.length; _musicPlayer.FadeInDuration = fadeIn; _musicPlayer.FadeOutDuration = fadeOut; _musicPlayer.VolumeRatio = MusicVolume; _musicPlayer.Play(); } public void StopMusic() { _musicPlayer?.Stop(); } public void PlayAudio(string musicName, float fadeIn = 0f, float fadeOut = 0f, bool isLoop = false) { if (!_clips.ContainsKey(musicName)) return; var player = GetPlayer(); if (_clips[musicName].Count > 1) { player.Clip = _clips[musicName][1]; _clips[musicName].RemoveAt(1); } else player.Clip = Object.Instantiate(_clips[musicName][0]); if (!player.Clip) { LogSystem.LogError($"音频资源 {musicName} 未找到或加载失败!"); return; } player.MusicName = musicName; player.IsLoop = isLoop; player.Length = _musicPlayer.Clip.length; player.FadeInDuration = fadeIn; player.FadeOutDuration = fadeOut; _musicPlayer.VolumeRatio = AudioVolume; player.Play(); } private AudioPlayer GetPlayer() { foreach (var player in _allPlayer) { if (player.State == PlayerState.Finished || player.State == PlayerState.Prepare) return player; } var sourceObj = new GameObject(); sourceObj.transform.SetParent(AudioRoot.transform); var source = sourceObj.AddComponent(); var newPlayer = new AudioPlayer(source); _allPlayer.Add(newPlayer); return newPlayer; } //----- InGame部分的音频 ------- private Main _main; private MapData _map; public void InGameAudioInit(Main main, MapData map) { _main = main; _map = map; } public void CalculateAndPlayAmbient() { if (_map == null) return; //播放环境音效。如果领土内有海洋,则60%概率播放海洋。否则必播放forest bool isForest = true; var player = _map.PlayerMap.SelfPlayerData; var gset = _map.GetPlayerTerritoryGridIdSet(player.Id); foreach (var g in gset) { if (!_map.GridMap.GetGridDataByGid(g, out var grid)) continue; if (grid.Terrain != TerrainType.Land) { isForest = Random.Range(0, 100) < 50 ? true : false; break; } } if(isForest) PlayAudio("SFX/ENV_forest",1f); else PlayAudio("SFX/ENV_sea",1f); } public void InGameOnTurnStart() { CalculateAndPlayAmbient(); } } public enum PlayerState { Prepare, FadeIn, FadeOut, Playing, Finished, } public class AudioPlayer { public string MusicName; public AudioSource Source; public AudioClip Clip; public bool IsLoop; public float Length; public float FadeInDuration; public float FadeOutDuration; // 音量 0-1 public float VolumeRatio; public float StartTime; public float EndTime; public PlayerState State; public AudioPlayer(AudioSource source) { Source = source; State = PlayerState.Prepare; } public void Play() { StartTime = Time.time; Source.time = 0; Source.clip = Clip; Source.volume = 0; Source.loop = IsLoop; Source.Play(); State = PlayerState.FadeIn; } public void Update() { if (Source == null) return; UpdateSourceVolume(); } public void Stop() { if (State == PlayerState.Finished || State == PlayerState.Prepare) return; State = PlayerState.FadeOut; EndTime = Time.time; } private void UpdateSourceVolume() { if (State == PlayerState.Finished || State == PlayerState.Prepare) return; if (State == PlayerState.FadeIn) { if (Time.time - StartTime < FadeInDuration) { Source.volume = (Time.time - StartTime) / FadeInDuration * VolumeRatio; } else { Source.volume = 1 * VolumeRatio; State = PlayerState.Playing; } } if (State == PlayerState.Playing && !IsLoop) { if (Time.time - StartTime >= Length - FadeOutDuration) { EndTime = Time.time; State = PlayerState.FadeOut; } } if (State == PlayerState.FadeOut) { if (Time.time - EndTime >= FadeOutDuration) { Source.volume = 0; Source.Stop(); State = PlayerState.Finished; } else { Source.volume = (FadeOutDuration - Time.time + EndTime) / FadeOutDuration * VolumeRatio; } } } } }