音频系统
Fink Framework 的音频系统基于 Unity AudioMixer 实现集中式音频管理, 提供 背景音乐(Music) 与 音效(SFX) 两大类音频的完整控制能力。
系统通过 ResManager 异步加载音频资源、通过对象池创建与回收 AudioSource,
并支持句柄(AudioOperation)获取进度与回调。
这是一个高度模块化、可扩展的音频系统。
1. 系统概述
AudioManager 包含以下核心组件:
1.1. 全局 AudioMixer
位于 Resources/Audio/MasterMixer
内部包含两个分组:
- Music(背景音乐)
- SFX(音效)
系统通过 FindMatchingGroups 自动获取分组。
1.2 背景音乐(Music)
- 使用专属持久化的
MusicPlayer(AudioSource) - 永不销毁(DontDestroyOnLoad)
- 输出到 Music MixerGroup
- 支持同步 / 异步 / 句柄方式播放
用途:循环播放背景音乐。
1.3 音效(Sound Effects / SFX)
- 每次播放使用对象池创建 AudioSource
- 输出到 SFX MixerGroup
- 支持父对象挂载(用于跟随 3D 声源)
- 播放结束后自动清理(FixedUpdate → CleanAudioSource)
- 支持同步 / 异步 / 回调 / 句柄方式播放
所有音效节点默认挂在:SoundPlayers
会在第一次播放音效时自动创建。
2. Mixer 初始化流程
AudioManager 构造函数中执行:InitMixer()
流程:
-
- 从 ResManager 加载
res://Audio/MasterMixer
- 从 ResManager 加载
-
- 查找 Music 组
-
- 查找 SFX 组
-
- 若加载失败会打印错误日志
之后所有 AudioSource 会自动绑定到对应 MixerGroup。
3. 背景音乐(Music)
背景音乐由一个全局唯一的 MusicPlayer 播放。
3.1 播放音乐(同步加载)
AudioManager.Instance.PlayMusic("res://Audio/Music/BGM_Main");
行为说明:
- 若 MusicPlayer 不存在则创建
- 挂到 DontDestroyOnLoad
- 使用 Music MixerGroup
- 同步加载 AudioClip
- 自动循环播放
3.2 播放音乐(异步 await)
await AudioManager.Instance.PlayMusicAsync("res://Audio/Music/BGM_Main");
返回 AudioSource。
内部行为:
- 异步加载音频资源(LoadAsyncHandle)
- 播放完成后设定循环
- 返回播放器引用
3.3 播放音乐(异步 回调)
AudioManager.Instance.PlayMusicAsync(
"res://Audio/Music/BGM_Main",
source =>
{
// 播放成功后的回调
}
);
3.4 播放音乐(异步 句柄)
var op = AudioManager.Instance.PlayMusicHandle("res://Audio/Music/BGM_Main");
op.Completed += o => Debug.Log("Music Loaded");
AudioOperation 可用于:
- Progress 进度条
- IsDone
- IsFailed
- Completed 回调
3.5 停止音乐
AudioManager.Instance.StopMusic("anyName");
(名称不参与逻辑,仅作接口兼容)
3.6 暂停音乐
AudioManager.Instance.PauseMusic("anyName");
3.7 修改背景音乐音量
AudioManager.Instance.ChangeMusicValue(0.5f);
内部将 01 转换为 -800 dB 的真实音量范围。
4. 音效(SFX)
音效使用对象池管理,每次播放都会从池中取出 AudioSource,并在播放结束后自动回收。
4.1 播放音效(同步加载)
AudioManager.Instance.PlaySound("res://Audio/SFX/Click");
4.2 播放音效(异步 await)
var source = await AudioManager.Instance.PlaySoundAsync("res://Audio/SFX/Explosion");
4.3 播放音效(异步 回调)
AudioManager.Instance.PlaySoundAsyncCallback(
"res://Audio/SFX/Explosion",
source => { Debug.Log("Loaded"); }
);
4.4 播放音效(异步 句柄)
var op = AudioManager.Instance.PlaySoundHandle("res://Audio/SFX/Explosion");
op.Completed += o =>
{
Debug.Log("Done: " + o.Progress);
};
句柄方式适合:
- Loading UI
- 多步骤流程
- 动画触发
- 播放前修改参数
4.5 停止某个音效
AudioManager.Instance.StopSound(source);
操作:
- 停止播放
- 清空 Clip
- 回收至对象池
- 从内部列表/集合移除记录
4.6 调整所有 SFX 音量
AudioManager.Instance.ChangeSoundValue(0.3f);
映射成 Mixer dB 范围 -80~0。
4.7 开关所有音效
AudioManager.Instance.ToggleAllSounds(true); // 恢复播放
AudioManager.Instance.ToggleAllSounds(false); // 全部停止
暂停后再次开启会重新播放非循环音效。
4.8 清空所有音效
AudioManager.Instance.ClearSound();
框架内置的场景切换流程会自动调用。
5. 内部机制说明
5.1 异步播放流程(核心)
所有异步音频最终走到:PlayAudioAsyncWrapper()
流程:
- 创建或获取 AudioSource
- 调用 ResManager.LoadAsyncHandle 加载 AudioClip
- 将进度同步给 AudioOperation.Progress
- 加载失败 → op.SetFailed()
- 加载成功 → 绑定 Clip 并播放
- op.SetResult(clip) → 触发 Completed
5.2 自动清理音效
FixedUpdate 中执行:CleanAudioSource()
倒序遍历 soundList:
若 AudioSource 不存在或未播放(!isPlaying)
清空 clip
回收至池
从集合与列表中移除
保证音效不会泄漏。
5.3 音效挂载结构
如果未传父对象:
SoundPlayers ↳ (pool instances)
若已传父对象:
Player ↳ ExplosionSource
用于 3D 声源跟随。
5.4 避免重复记录播放源
音效播放使用:
HashSet<AudioSource> soundSet
List<AudioSource> soundList
好处:
- soundSet → O(1) 判断是否重复
- soundList → 有序遍历并倒序删除
这是避免重复回收、重复检测的关键结构。
6. 音频操作句柄(AudioOperation)
异步播放会返回一个 AudioOperation。
包含:
| 属性 | 说明 |
|---|---|
IsDone | 是否完成 |
IsFailed | 是否失败 |
Progress | 0~1 进度(来自资源加载) |
Clip | 加载成功的 AudioClip |
Source | 播放的 AudioSource |
Completed | 完成事件回调 |
等待句柄完成: await op.WaitUntilDone();
7. 最佳实践(建议)
背景音乐必须放在 res://Audio/Music/
音效必须放在 res://Audio/SFX/
所有音效尽量使用异步加载,避免同步卡顿
切换场景前调用 ClearSound()(框架场景系统已自动处理)
需要淡入淡出可在 MusicPlayer 上自行扩展 Tween
8. 总结
本音频系统具备:
- 完整的同步/异步/句柄式播放能力
- Mixer 级别精确音量控制
- 对象池优化的音效管理
- 自动清理减少泄漏风险
- 适用于 2D/3D 游戏、UI、VR 项目