地堡留声机 BGM
本模块用于自定义地堡主页和主菜单的背景音乐,支持播放列表、随机播放、自动切歌、音量控制等功能。模块会拦截游戏的留声机系统,使用自定义音乐替换原版 BGM。
功能概述
- 替换地堡留声机 BGM(HomeBGM 文件夹中的所有音乐)
- 支持播放列表(任意数量的音乐文件)
- 自动切歌(曲目结束后自动播放下一首)
- 随机播放(支持避免立即重复)
- 音量控制(0-100%)
- 总线切换(Music 总线或 SFX 总线)
- ModConfig UI 热重载配置
快速开始
最小可用示例(地堡留声机)
假设你想在地堡主页播放自定义音乐:
- 创建目录结构:
DuckovCustomSounds/
└─ HomeBGM/
├─ 曲名1 - 艺术家1.mp3
├─ 曲名2 - 艺术家2.mp3
└─ 曲名3 - 艺术家3.mp3进入游戏,进入地堡主页,观察留声机是否播放自定义音乐
使用留声机的"下一首"/"上一首"按钮切换音乐
文件命名规则
地堡留声机 BGM
文件路径:
DuckovCustomSounds/
└─ HomeBGM/
├─ 曲名1 - 艺术家1.mp3
├─ 曲名2 - 艺术家2.mp3
└─ 曲名3 - 艺术家3.mp3文件名格式:
推荐格式:
曲名 - 艺术家.mp3- 例如:
Never Gonna Give You Up - Rick Astley.mp3 - 模块会自动解析为:曲名 = "Never Gonna Give You Up",艺术家 = "Rick Astley"
- 例如:
简单格式:
曲名.mp3- 例如:
My Favorite Song.mp3 - 模块会解析为:曲名 = "My Favorite Song",艺术家 = "群星"
- 例如:
说明:
- 文件名可以任意,但推荐使用
曲名 - 艺术家.mp3格式 - 支持任意数量的音乐文件
- 留声机会显示解析后的曲名和艺术家
支持的音频格式
目前仅支持 .mp3 格式。
ModConfig UI 配置
模块支持通过 ModConfig UI 进行热重载配置(无需重启游戏):
NOTE
目前 ModConfig UI 支持尚未完善,你看到的属于前瞻性内容。
配置项:启用主页BGM
- 启用或禁用整个模块
- 默认:启用
配置项:启用进入基地音效 (start.mp3)
- 启用或禁用进入基地时的音效
- 默认:启用
- 注意:此功能需要
start.mp3文件(当前文档未涉及)
配置项:音乐音量 (%)
- 控制 HomeBGM 的音量(0-100%)
- 默认:100%
- 变更会立即应用到当前播放的音乐
配置项:使用 SFX 总线播放(实验性)
- 将音乐从 Music 总线切换到 SFX 总线
- 默认:禁用(使用 Music 总线)
- 用途:可能改善立体声效果,但受 SFX 音量控制影响
- 变更会重新播放当前音乐
配置项:随机播放(Next)
- 启用后,点击"下一首"会随机选择音乐
- 默认:禁用(顺序播放)
配置项:上一曲也随机
- 启用后,点击"上一首"也会随机选择音乐
- 默认:禁用(顺序播放)
- 需要先启用"随机播放(Next)"
配置项:避免连续重复同一曲目
- 启用后,随机播放时不会连续播放同一首音乐
- 默认:启用
- 仅在随机播放模式下生效
配置项:自动播放下一曲
- 启用后,曲目结束时自动播放下一首
- 默认:启用
- 禁用后,曲目结束时会循环播放当前曲目
settings.json 配置
当 ModConfig UI 不可用时,模块会从 settings.json 读取配置。配置文件位于:
DuckovCustomSounds/settings.json可用字段
字段:homeBgmAutoPlayNext
- 类型:
boolean - 默认值:
true - 说明:控制曲目结束后是否自动播放下一首。
true表示自动切歌,false表示单曲循环
字段:homeBgmRandomEnabled
- 类型:
boolean - 默认值:
false - 说明:启用随机播放模式。
true表示点击"下一首"时随机选择,false表示顺序播放
字段:homeBgmRandomNoRepeat
- 类型:
boolean - 默认值:
true - 说明:避免连续重复同一曲目。仅在
homeBgmRandomEnabled=true时生效
字段:homeBgmRandomizePrevious
- 类型:
boolean - 默认值:
false - 说明:点击"上一首"时也随机选择。仅在
homeBgmRandomEnabled=true时生效
完整配置示例
示例 1:顺序播放 + 自动切歌(默认)
{
"homeBgmAutoPlayNext": true,
"homeBgmRandomEnabled": false,
"homeBgmRandomNoRepeat": true,
"homeBgmRandomizePrevious": false
}示例 2:随机播放 + 避免重复
{
"homeBgmAutoPlayNext": true,
"homeBgmRandomEnabled": true,
"homeBgmRandomNoRepeat": true,
"homeBgmRandomizePrevious": false
}示例 3:单曲循环
{
"homeBgmAutoPlayNext": false,
"homeBgmRandomEnabled": false,
"homeBgmRandomNoRepeat": true,
"homeBgmRandomizePrevious": false
}示例 4:完全随机(包括上一首)
{
"homeBgmAutoPlayNext": true,
"homeBgmRandomEnabled": true,
"homeBgmRandomNoRepeat": true,
"homeBgmRandomizePrevious": true
}配置优先级
模块按以下优先级加载配置:
- ModConfig UI 配置(如果可用)- 最高优先级,支持热重载
- settings.json 配置 - 回退方案,需要重启游戏生效
注意:
- 如果 ModConfig UI 可用,settings.json 中的这些字段会被忽略
- 如果 ModConfig UI 不可用,模块会自动使用 settings.json 配置
- 修改 settings.json 后需要重启游戏才能生效
目录结构示例
完整示例(地堡留声机)
DuckovCustomSounds/
└─ HomeBGM/
├─ Never Gonna Give You Up - Rick Astley.mp3
├─ Bohemian Rhapsody - Queen.mp3
├─ Hotel California - Eagles.mp3
└─ Stairway to Heaven - Led Zeppelin.mp3常见问题
1. 地堡留声机不播放自定义音乐?
可能原因:
HomeBGM/文件夹不存在或为空- 文件格式不是
.mp3 - 模块未启用
解决方法:
- 确认
HomeBGM/文件夹存在且包含至少一首.mp3文件 - 确认 ModConfig UI 中"启用主页BGM"已启用
- 启用 Debug 日志,查看加载信息
2. 切歌时有爆音或卡顿?
可能原因:
- 音乐文件的采样率或响度不统一
- 音乐文件没有淡出
解决方法:
- 使用音频编辑软件(如 Audacity)统一采样率(建议 44.1kHz 或 48kHz)
- 统一响度(建议归一化到 -3dB 到 0dB)
- 在音乐结尾添加 0.5-1 秒的淡出
3. 自动切歌不工作?
可能原因:
- ModConfig UI 中"自动播放下一曲"未启用
- 音乐文件循环播放(自动切歌时不会循环)
解决方法:
- 确认 ModConfig UI 中"自动播放下一曲"已启用
- 等待当前曲目播放完成(不要手动停止)
- 启用 Debug 日志,查看自动切歌信息
4. 随机播放总是重复同一首?
可能原因:
- 只有一首音乐
- "避免连续重复同一曲目"未启用
解决方法:
- 确认
HomeBGM/文件夹中有多首音乐 - 确认"避免连续重复同一曲目"已启用
5. 音量太小或太大?
可能原因:
- ModConfig UI 中的音量设置不合适
- 音乐文件本身的音量不合适
- 使用 SFX 总线时受游戏 SFX 音量影响
解决方法:
- 调整 ModConfig UI 中的"音乐音量 (%)"
- 使用音频编辑软件调整音乐文件的音量
- 如果使用 SFX 总线,调整游戏内 SFX 音量
6. 留声机显示的曲名或艺术家不正确?
可能原因:
- 文件名格式不符合
曲名 - 艺术家.mp3
解决方法:
- 重命名文件为
曲名 - 艺术家.mp3格式 - 确保使用英文减号
-(不是中文破折号—) - 重新加载模块(重启游戏)
7. 场景切换后音乐还在播放?
这是异常情况。模块会在以下时机自动停止音乐:
- 场景切换
- 进入战斗
- 调用
AudioManager.StopBGM()
如果音乐没有停止,请报告 Bug。
技术细节
留声机拦截
模块使用 Harmony 补丁拦截以下方法:
留声机系统拦截:
BaseBGMSelector.Awake():重建entries列表,添加自定义 BGMBaseBGMSelector.Set(int, bool, bool):拦截播放,使用自定义音乐BaseBGMSelector.SetNext():处理随机模式和索引循环BaseBGMSelector.SetPrevious():处理随机模式和索引循环BaseBGMSelector.Set(string):拦截 UI 直接点击条目
场景切换保护:
AudioManager.StopBGM():场景切换时停止自定义音乐MultiSceneCore.LocalOnSubSceneWillBeUnloaded():场景卸载时停止音乐
Stinger 抑制:
AudioObject.Post(string, bool):抑制留声机 Stinger 事件(仅在主基地/主菜单)
自动切歌机制
- 播放开始:播放音乐时,设置
loop=false(不循环单曲) - 状态检测:每帧检查播放状态(PLAYING → STOPPED)
- 武装机制:检测到 PLAYING 状态后武装,检测到 STOPPED 状态后触发切歌
- 切换下一首:调用
PlayNextHomeBGM(),根据随机模式选择下一首 - 防误触发:只有武装后才会触发自动切歌
音频通道
模块支持两种音频通道:
Music 总线(默认):
- 使用
AudioManager.bgmSource.PostFile() - 事件路径:
Music/custom_loop(循环)或Music/custom(不循环) doRelease=false:保持实例有效,便于自动切歌检测- 优点:不受 SFX 音量影响
- 缺点:可能与其他 BGM 冲突
SFX 总线(实验性):
- 使用
AudioManager.stingerSource.PostCustomSFX() - 优点:可能改善立体声效果
- 缺点:受游戏 SFX 音量控制影响
文件名解析
模块会自动解析文件名:
格式 1:曲名 - 艺术家.mp3
- 解析为:
name = "曲名",author = "艺术家" - 例如:
Never Gonna Give You Up - Rick Astley.mp3name = "Never Gonna Give You Up"author = "Rick Astley"
格式 2:曲名.mp3
- 解析为:
name = "曲名",author = "群星" - 例如:
My Favorite Song.mp3name = "My Favorite Song"author = "群星"
随机播放算法
避免立即重复:
- 排除当前索引的等概率采样
- 算法:
r = Random.Range(0, count - 1); if (r >= current) r++; - 确保不会连续播放同一首音乐
不避免重复:
- 等概率采样:
r = Random.Range(0, count);
实例管理
模块会追踪当前播放的音乐实例:
_currentBGMInstance:当前播放的 BGM 实例
在以下时机会停止实例:
- 场景切换
- 播放新音乐
- 模式切换(热重载)
递归保护
模块使用多种机制防止递归调用:
_isStoppingBGM:防止StopCurrentBGM()递归_isProcessingSet:防止Set()重入_manualInvokeFlag:标记手动切歌,确保信息气泡显示
排错指引
启用详细日志
编辑 settings.json:
{
"logLevels": {
"HomeBGM": "Debug"
}
}查看日志文件
日志位于:DuckovCustomSounds/Logs/
搜索关键字:
找到主菜单音乐:主菜单 BGM 加载在 HomeBGM 中找到:HomeBGM 文件夹扫描加载 BGM 元数据:音乐文件解析拦截到 mus_title:主菜单 BGM 拦截已重建 BaseBGMSelector.entries:留声机列表重建拦截留声机播放自定义 BGM:留声机播放拦截自动切歌:自动切歌触发
常见错误信息
未找到主菜单音乐文件(可选):TitleBGM/title.mp3不存在未找到大厅音乐文件夹(可选):HomeBGM/文件夹不存在播放主菜单 BGM 失败:播放主菜单 BGM 时出错播放 Home BGM 失败:播放地堡留声机 BGM 时出错播放自定义 BGM 失败:播放自定义音乐时出错
测试建议
- 先使用最简单的配置(只提供 1-2 首音乐)
- 确认模块已启用(ModConfig UI)
- 使用
.mp3格式(兼容性最好) - 启用 Debug 日志,观察加载和播放过程
- 测试不同场景:
- 主菜单
- 地堡主页
- 留声机切歌
- 自动切歌
- 场景切换
性能优化
模块已进行以下性能优化:
- 日志字符串仅在对应级别启用时才构建
- 使用弱引用避免内存泄漏
- 自动清理音频资源
- 防重入保护避免递归调用
