Files
mtgodot-poc/audit/contracts/audio.bgm-ambience-3d.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

105 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# audio.bgm_ambience_3d —— BGM、区域环境音、角色音效、3D 监听器和前后台恢复
状态:`PARTIAL`
本合同检查 40250 的 2D/3D/Stream 三类声音实例、BGM 音乐状态机、区域环境声、角色动作音效、监听器坐标/方向、音量曲线、限频和前后台生命周期。判断标准是播放实例的分配、状态转换、坐标/音量公式、重复播放限制、地图卸载和恢复副作用一致;“测试能播放”不等于 SoundManager 生命周期等价。
## 40250 参考调用链
- `MilesLib/SoundManager.cpp` / `SoundManager2D.cpp` / `SoundManager3D.cpp` / `SoundManagerStream.cpp`
- `Create` 按 2D、Stream、3D 顺序初始化驱动;`Destroy` 先停止音乐和 3D 实例,再关闭 listener/provider/driver。
- 2D/3D 音效使用固定实例池,实例完成后才可复用;3D 位置按 `(world-listener)/soundScale`,环境声使用 `ambienceSoundScale`。
- 角色音效可按 5000cm 半径过滤,并按文件名限制 0.3 秒内重复播放;`StopAllSound3D` 同时清空历史。
- `MilesLib/SoundManager.cpp`
- BGM 维护多个 `TMusicInstance`,状态为 OFF、FADE_IN、FADE_LIMIT_OUT、FADE_OUT、PLAY;`Update` 每帧递增/递减音量,达到边界后切换状态或停止实例。
- `FadeInMusic` 先淡出已有音乐;`FadeOutAllMusic` 为每个活动实例设置淡出;`FadeLimitOutMusic` 保留目标音量。
- `SaveVolume` 同时把音乐和音效音量置零并保存备份,`RestoreVolume` 恢复两者;不是只停止或只静音 BGM。
- `MilesLib/SoundManager3D.cpp` / `PythonSoundManagerModule.cpp`
- 暴露 `PlaySound`、`PlaySound3D`、`PlayMusic`、FadeIn/FadeOut、StopAllSound、音乐/音效音量、SoundScale 和 AmbienceSoundScale。
- 3D listener 维护位置、方向、up 和速度;环境声由 `CArea::UpdateAroundAmbience` 每次更新。
- `GameLib/Area.cpp` / `Area.h`
- `TAmbienceInstance` 从 AreaAmbienceData/Property 读取位置、范围、最大音量半径、LOOP/ONCE/STEP 和步进随机间隔。
- LOOP 在范围内创建循环实例并按距离线性衰减;离开范围停止并清理索引。ONCE 只在进入范围时播放,STEP 按时间到期播放,离开时重置下一次时间。
## 当前端实现映射
- `project/audio.gd`
- 已有 BGM 双播放器、UI 音效池、AudioStreamPlayer3D、listener 重定位、200/1000 缩放、音量比例曲线、`.mss` 60FPS 事件、角色音效距离/0.3 秒限频,以及 Area LOOP/ONCE/STEP。
- 3D 播放器按需创建,没有 40250 固定实例池/Provider 生命周期;Godot `unit_size/max_distance` 衰减也不是 Miles provider 的同一声学模型。
- BGM 使用 Tween 交叉淡入淡出,不是 `TMusicInstance::Update` 的逐帧状态机;加载失败时 `_bgm_name` 先写入,存在“资源失败后同名请求被跳过”的状态风险。
- `project/bgm_director.gd` / `project/game_scene.gd`
- 已按 `bgm_changed` 和 `world_reset` 选曲,服务器音量优先,缺省回落 `musicInfo.fieldMusic`,并保存最近战场曲目。
- 生产链已创建 Audio、BgmDirector 并在每帧更新 listener/区域环境声;地图切换时会停止 BGM,但没有完整证明参考端多音乐实例的淡出状态被逐个清理。
- `project/app_lifecycle.gd`
- 已在后台保存并恢复音量、暂停树、降低 FPS,保持 BGM 名称。
- 后台入口调用 `save_volume` 后同步执行 `set_music_volume(0.0)` 和 `set_sound_volume(0.0)`;恢复仍由 `restore_volume` 一次恢复两类音量。
- `project/game_scene.gd`
- `_exit_tree` 在卸载 native world 前调用 `Audio.shutdown()`,停止 BGM/3D 音效、解绑播放器 stream 并清空声音缓存,覆盖退出时的资源释放边界。
- `formats/area_data.*` / `extension/src/metin2_world.cpp`
- 已读取 AreaAmbienceData、`.pra` 属性和 `playtype/range/maxvolume/playinterval`,按流式 tile 暴露 ambience source;world unload 会移除来源。
- 来源数据和播放状态由不同节点维护,地图卸载/快速切图时音频实例和来源索引的生产级清理尚未有组合测试。
## 分支等价矩阵
| 40250 判据/副作用 | 当前实现 | 结论 |
|---|---|---|
| 2D/UI/3D/Stream 三类初始化、实例复用和销毁 | Audio 创建 BGM/UI/3D 节点,使用 Godot 动态播放器 | 部分等价;缺少固定实例池、Miles provider/driver 生命周期和完整销毁证据 |
| BGM 多实例 OFF/FADE_IN/FADE_LIMIT_OUT/FADE_OUT/PLAY 状态机 | 双播放器 Tween 交叉淡入淡出 | 部分等价;目标可听结果近似,但状态/边界/多个音乐实例和每帧速度公式不一致 |
| FadeIn 先处理旧曲、FadeLimitOut、FadeOutAll | 有对应方法和淡出 Tween | 部分等价;没有逐实例状态快照和停止时序证明,加载失败状态回滚也不完整 |
| 音量比例/等级曲线、音乐/音效独立音量 | `ratio_to_apply_volume`、grade 和 UI 设置测试通过 | 基础公式等价;后台 BGM/SFX 同时静音与恢复已有回归,真实 3D 播放实例仍需独立证据 |
| SaveVolume/RestoreVolume 同时静音和恢复 BGM/SFX | 保存两类值,后台同时调用 `set_music_volume(0)`/`set_sound_volume(0)`,前台调用 `restore_volume` | 基础行为等价;真实 Miles provider 实例和 3D 播放器清理仍需独立证据 |
| listener 位置/方向、200/1000 缩放和 3D 播放 | `GameScene._process` 更新 listener,Audio 重定位实例 | 部分等价;单位与主要公式存在,Godot attenuation/provider 行为不是 Miles 1:1 |
| 角色动作 `.mss` 事件、5000cm 距离和 0.3s 限频 | PlayerView/Audio 已实现,相关测试通过 | 基础路径通过;远端角色、全部动作事件和实例池耗尽/失败回退未覆盖 |
| Area LOOP/ONCE/STEP、距离衰减和离开范围清理 | world source + `update_ambience_sources` 已实现 | 部分等价;A3 真实来源测试通过,但快速换图、音频实例释放和多 source 乱序缺少证据 |
| 地图 BGM/区域声/音效在切图、失焦、暂停、重连后恢复 | BGM 停止、listener/ambient 每帧更新、后台 BGM/SFX 恢复存在 | 部分等价;跨图活动实例清理和真实 3D 播放器释放尚未形成生产级闭环 |
## 已执行测试
- `project/audio_miles_test.gd`:通过;覆盖 200/1000 缩放、音量曲线、`.mss` 解析、角色音效距离/限频和环境声实例基本路径。
- `project/ambience_test.gd`:通过;A3 真实地图来源解析出 1 个 LOOP 环境声,范围、类型和声音路径保持。
- `project/bgm_test.gd`:通过;覆盖地图 BGM 选曲、服务端音量、默认回落、换图信号,以及前后台 BGM/SFX 音量保存、同时静音和恢复。
- `project/rendering_scenario_test.gd`:通过;Debug 原生库退出无 `ObjectDB` 音频资源泄漏,验证 `GameScene._exit_tree -> Audio.shutdown` 的退出边界。
- `project/test_bgm_jukebox_parity.gd`:18 passed、0 failed;覆盖点唱机曲目注册、停止、音量钳制、地图默认和覆写恢复,不覆盖真实 Audio 实例状态机。
- `project/app_flow_lifecycle_test.gd`:通过;覆盖 AppLifecycle 单一属主和断线重连流程,不覆盖真实音频实例释放。
- `project/system_option_ui_test.gd`:通过;覆盖音量设置控件绑定,不覆盖后台和播放失败边界。
## Deep audit round 2026-09-21
### Fresh regression results
- `audio_miles_test.gd`、`ambience_test.gd`、`bgm_test.gd`、`test_bgm_jukebox_parity.gd` 18/18、`app_flow_lifecycle_test.gd`、`system_option_ui_test.gd` 均退出码 0。
- 回归覆盖了 3D 缩放/限频、A3 环境声、地图 BGM 选择、点唱机覆盖/恢复、生命周期属主和设置绑定;FakeAudio 仍不能证明真实播放实例状态机。
### Static parity conclusion
- BGM/区域环境声/角色 `.mss` 音效的主要入口存在,但 Godot 动态播放器/Tween 不等价于 40250 固定实例池、Provider 和 `TMusicInstance` 状态机。
- `app_lifecycle.gd` 后台路径已同步静音并恢复 BGM/SFX;加载失败后 `_bgm_name` 可能阻止同名重试,跨图快速切换时来源索引和播放器释放仍缺少生产级证据。
- listener、缩放和 0.3s/5000cm 规则有局部验证,但远端 Actor 动作音效、实例池耗尽、失焦/暂停/重连/退出清理和 Godot attenuation 与 Miles provider 的边界未闭合。
### Active source group review
- 对 Visual Studio `MilesLib` 活跃编译单元逐组核对后,参考链补充确认:`CSoundManager2D/3D/Stream::Initialize/Destroy` 创建并释放固定实例池;`CSoundData` 维护 pack-backed 数据缓存和 access/play time;`CSoundInstance2D/3D/Stream` 分别处理 SetSound、Play/Stop、位置/方向/速度与音量;`CSoundManager` 负责 listener、距离缩放、0.3 秒限频、环境声、音乐实例索引和多状态淡入淡出。
- 当前端 Audio 的动态播放器和 Tween 只覆盖可听结果的部分路径,尚未证明固定池、sound data cache、播放失败/空槽、逐实例停止和 MusicIndex 状态机等副作用一致;本组静态核对完成,合同仍为 `PARTIAL`。
## Implementation fix round 2026-09-22T08:50Z
- 修复前 `bgm_test.gd` 新增的 40250 `SaveVolume` 回归失败:进入后台后 `master_bgm=0`,但 `master_sfx=0.9`。
- 按 `CSoundManager::SaveVolume` 的顺序,`AppLifecycle._enter_background` 在保存备份后同时调用 `set_music_volume(0.0)` 与 `set_sound_volume(0.0)`;`_exit_background` 继续由 `restore_volume` 同时恢复两者。
- 修复后 `bgm_test.gd`、`audio_miles_test.gd`、`app_flow_lifecycle_test.gd`、`disconnect_world_reset_test.gd`、`netbridge_test.gd` 均通过;渲染退出回归无 `ObjectDB` 泄漏。
- 该轮只闭合后台音量副作用和退出音频缓存清理,BGM 多实例状态机、Miles 固定实例池、真实 3D 衰减/Provider、快速换图来源清理仍保持 `PARTIAL`。
本轮仍为 `PARTIAL`,完成后台音量和退出清理的实现及第二轮证据登记。
## 已确认差异和未验证项
1. 验证正在播放的 3D 实例不会绕过 master SFX,并补真实设备/实例路径的后台静音回归。
2. 建立 BGM 多实例状态快照测试,覆盖 FadeIn、FadeLimitOut、FadeOut、FadeOutAll、相同曲目重入、加载失败回滚和切图清理。
3. 明确 Godot 3D 衰减与 Miles provider 的适配边界,至少固定 `unit_size/max_distance`、监听器方向/速度和实例池耗尽行为。
4. 增加真实流式地图快速切换、来源删除、重复 key、LOOP/ONCE/STEP 乱序和退出时无悬挂播放器的测试。
5. 补齐远端 Actor 的动作音效触发、重连/死亡/换图清理和资源缺失回退证据。
## 下一轮验证入口
- 先审计 `lifecycle.platform_release`,核对 Windows 窗口、导出、自包含资源、崩溃处理和发布门禁。
- 回到实现阶段时,优先建立 BGM 多实例状态机和真实 3D 播放器清理测试,再处理固定实例池/Provider 的适配边界。