using UnityEngine;
using System.Collections.Generic;
///
/// 战斗音效管理器
/// 职责:播放战斗中的短促技能动作和特效音效
///
public class BattleSoundManager
{
private BattleField battleField;
private GameObject audioSourceObject;
// 音效池配置
private const int INITIAL_AUDIO_POOL = 15; // 初始池大小
private const int MAX_AUDIO_SOURCES = 30; // AudioSource 总数上限
private Queue audioSourcePool = new Queue();
private List activeAudioSources = new List();
// 同一音效同时播放的最大数量
private const int MAX_SAME_AUDIO_COUNT = 3;
// 记录每个音效ID当前播放的AudioSource
private Dictionary> audioIdToSources = new Dictionary>();
// 音频剪辑缓存
private Dictionary audioClipCache = new Dictionary();
// 当前播放速度
private float currentSpeedRatio = 1f;
// 是否有焦点
private bool hasFocus = true;
public BattleSoundManager(BattleField _battleField)
{
battleField = _battleField;
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 构造函数 - 初始焦点状态={battleField.IsFocus()}");
InitializeAudioSources();
// 监听战场速度变化
if (battleField != null)
{
battleField.OnSpeedRatioChange -= OnSpeedRatioChanged;
battleField.OnSpeedRatioChange += OnSpeedRatioChanged;
battleField.OnFocusChange -= OnFocusChanged;
battleField.OnFocusChange += OnFocusChanged;
currentSpeedRatio = battleField.speedRatio;
hasFocus = battleField.IsFocus();
}
}
///
/// 初始化音频源池
///
private void InitializeAudioSources()
{
// 使用战场根节点作为 AudioSource 的载体
audioSourceObject = battleField.battleRootNode.gameObject;
// 创建初始音频源池
for (int i = 0; i < INITIAL_AUDIO_POOL; i++)
{
var source = audioSourceObject.AddComponent();
source.playOnAwake = false;
source.loop = false;
source.spatialBlend = 0f; // 2D音效
audioSourcePool.Enqueue(source);
}
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 初始化了 {INITIAL_AUDIO_POOL} 个 AudioSource");
}
///
/// 每帧运行,清理已完成的音频源
///
public void Run()
{
// 每帧清理,性能开销很小
CleanupFinishedAudioSources();
}
///
/// 播放特效音效
///
/// 音效ID
public void PlayEffectSound(int audioId, bool pitchControl = true)
{
if (audioId <= 0)
{
return;
}
PlaySound(audioId, pitchControl);
}
///
/// 核心播放方法
///
private void PlaySound(int audioId, bool pitchControl)
{
// 检查是否有焦点,无焦点时不播放
if (!hasFocus)
{
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 无焦点,拒绝播放音效 {audioId}");
return;
}
// 检查该音效是否已达到播放上限
if (!CanPlayAudio(audioId))
{
return;
}
var audioClip = GetAudioClip(audioId);
if (audioClip == null)
{
return;
}
// 从池中获取可用的音频源
AudioSource source = GetAvailableAudioSource();
if (source == null)
{
return;
}
// 设置音量(使用音效音量设置)
source.volume = SystemSetting.Instance.GetSoundEffect();
// 设置播放速度,使用pitch来控制
// pitch范围建议在0.5-2.0之间以避免失真
if (pitchControl)
{
float pitch = Mathf.Clamp(currentSpeedRatio, 0.5f, 2.0f);
source.pitch = pitch;
}
else
{
source.pitch = 1.0f;
}
// 设置音频剪辑并播放(使用 Play() 而不是 PlayOneShot(),以便 isPlaying 状态正确)
source.clip = audioClip;
source.Play();
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 播放音效 {audioId} - {audioClip.name}");
// 标记为活跃
if (!activeAudioSources.Contains(source))
{
activeAudioSources.Add(source);
}
// 记录该音效ID与AudioSource的关联
if (!audioIdToSources.ContainsKey(audioId))
{
audioIdToSources[audioId] = new List();
}
audioIdToSources[audioId].Add(source);
}
///
/// 获取音频剪辑(优先从缓存获取)
///
private AudioClip GetAudioClip(int audioId)
{
// 先从缓存中查找
if (audioClipCache.TryGetValue(audioId, out AudioClip cachedClip))
{
return cachedClip;
}
// 缓存中没有,则加载并缓存
var audioClip = LoadAudioClip(audioId);
if (audioClip != null)
{
audioClipCache[audioId] = audioClip;
}
return audioClip;
}
///
/// 检查是否可以播放该音效
///
private bool CanPlayAudio(int audioId)
{
if (!audioIdToSources.ContainsKey(audioId))
{
return true;
}
// 过滤掉已经停止播放的AudioSource
var sources = audioIdToSources[audioId];
sources.RemoveAll(s => s == null || !s.isPlaying);
// 检查同时播放的数量
return sources.Count < MAX_SAME_AUDIO_COUNT;
}
///
/// 加载音频剪辑
///
private AudioClip LoadAudioClip(int audioId)
{
var config = AudioConfig.Get(audioId);
if (config == null)
return null;
AudioClip audioClip = ResManager.Instance.LoadAsset(
"Audio/" + config.Folder,
config.Audio,
false
);
return audioClip;
}
///
/// 获取可用的音频源
///
private AudioSource GetAvailableAudioSource()
{
// 尝试从池中获取
if (audioSourcePool.Count > 0)
{
return audioSourcePool.Dequeue();
}
// 如果池为空,检查是否可以动态创建
if (audioSourceObject == null)
{
BattleDebug.LogErrorError("BattleSoundManager: audioSourceObject 为空,无法创建 AudioSource");
return null;
}
// 计算当前总的 AudioSource 数量
int totalAudioSources = audioSourceObject.GetComponents().Length;
if (totalAudioSources >= MAX_AUDIO_SOURCES)
{
// 达到上限,不再创建,丢弃这次播放请求
BattleDebug.LogErrorWarning($"BattleSoundManager: AudioSource 数量已达上限 {MAX_AUDIO_SOURCES},无法播放新音效");
return null;
}
// 在 battleRootNode 上动态创建新的 AudioSource
var source = audioSourceObject.AddComponent();
source.playOnAwake = false;
source.loop = false;
source.spatialBlend = 0f; // 2D音效
return source;
}
///
/// 清理已播放完成的音频源
///
private void CleanupFinishedAudioSources()
{
// 清理 activeAudioSources 列表
for (int i = activeAudioSources.Count - 1; i >= 0; i--)
{
var source = activeAudioSources[i];
if (source == null || !source.isPlaying)
{
activeAudioSources.RemoveAt(i);
if (source != null)
{
// 清空 clip 引用,释放内存
source.Stop();
source.clip = null;
audioSourcePool.Enqueue(source);
}
}
}
// 清理 audioIdToSources 中已停止播放的 AudioSource
var keysToRemove = new List();
foreach (var kvp in audioIdToSources)
{
kvp.Value.RemoveAll(s => s == null || !s.isPlaying);
if (kvp.Value.Count == 0)
{
keysToRemove.Add(kvp.Key);
}
}
// 移除空的音效ID记录
foreach (var key in keysToRemove)
{
audioIdToSources.Remove(key);
}
}
///
/// 战场速度变化回调
///
private void OnSpeedRatioChanged(float newSpeedRatio)
{
currentSpeedRatio = newSpeedRatio;
// 更新所有正在播放的音效的速度
foreach (var source in activeAudioSources)
{
if (source != null && source.isPlaying)
{
float pitch = Mathf.Clamp(newSpeedRatio, 0.5f, 2.0f);
source.pitch = pitch;
}
}
}
///
/// 焦点变化回调
///
private void OnFocusChanged(bool isFocus)
{
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: OnFocusChanged({isFocus}) - 当前活跃音效数={activeAudioSources.Count}");
hasFocus = isFocus;
// 失去焦点时,停止所有正在播放的音效
if (!hasFocus)
{
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 失去焦点,准备停止所有音效");
StopAllSounds();
}
else
{
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 获得焦点");
}
}
///
/// 停止所有音效
///
public void StopAllSounds()
{
int activeCount = activeAudioSources.Count;
int totalStopped = 0;
// 不依赖列表,直接停止 GameObject 上的所有 AudioSource
if (audioSourceObject != null)
{
var allSources = audioSourceObject.GetComponents();
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: StopAllSounds - GameObject上共有 {allSources.Length} 个AudioSource");
foreach (var source in allSources)
{
if (source != null)
{
if (source.isPlaying)
{
BattleDebug.LogError($" 停止正在播放的: {source.clip?.name}");
totalStopped++;
}
source.Stop();
source.clip = null;
}
}
}
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: StopAllSounds - 活跃列表={activeCount}, 实际停止={totalStopped}");
// 清空所有列表
activeAudioSources.Clear();
audioIdToSources.Clear();
// 重建对象池
audioSourcePool.Clear();
if (audioSourceObject != null)
{
var allSources = audioSourceObject.GetComponents();
foreach (var source in allSources)
{
if (source != null)
{
audioSourcePool.Enqueue(source);
}
}
}
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: StopAllSounds 完成");
}
///
/// 设置音量
///
public void SetVolume(float volume)
{
volume = Mathf.Clamp01(volume);
foreach (var source in activeAudioSources)
{
if (source != null)
{
source.volume = volume;
}
}
}
///
/// 释放资源
///
public void Release()
{
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: Release 开始");
if (battleField != null)
{
battleField.OnSpeedRatioChange -= OnSpeedRatioChanged;
battleField.OnFocusChange -= OnFocusChanged;
}
StopAllSounds();
// 销毁所有 AudioSource 组件
if (audioSourceObject != null)
{
var sources = audioSourceObject.GetComponents();
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: 销毁 {sources.Length} 个 AudioSource 组件");
foreach (var source in sources)
{
if (source != null)
{
GameObject.Destroy(source);
}
}
audioSourceObject = null;
}
audioSourcePool.Clear();
activeAudioSources.Clear();
audioIdToSources.Clear();
audioClipCache.Clear();
BattleDebug.LogError($"BattleSoundManager [{battleField.guid}]: Release 完成");
}
}