# BaseGames 框架代码评审 v16 > **评审日期**:2026-05(会话 16) > **前置版本**:v15(得分 9.56/10,修复 TD-30~TD-34) > **本次变更性质**:针对 v15「已知可接受的设计选择」与「后续建议」的全量落地修复 > **发现问题**:TD-35(共 1 项)+ Suggestion 1~4(共 4 项),全部已修复 > **修复后得分**:**9.68 / 10** --- ## 一、本轮修复背景 v15 评审将以下条目划分为「可接受」或「后续建议」: | 分类 | 条目 | 处理结论 | |------|------|----------| | 可接受设计选择 #2 | `LocalizationManager.OnLanguageChanged` 静态事件向后兼容层 | 已判定为**需修复(TD-35)**:本项目是全新框架,不存在需要兼容的旧调用方,静态兼容层引入了不必要的双事件系统,应彻底移除 | | 后续建议 1 | `DialogueUI` 语音剪辑从未播放 | **已实施** | | 后续建议 2 | `QuestObjectiveSO.displayText` 为原始文本而非本地化 key | **已实施** | | 后续建议 3 | `ChallengeRoomManager` spawnPoint 为 null 时静默回退 Vector3.zero | **已实施** | | 后续建议 4 | `LoadingScreenManager._tipMessages` 存储直接字符串而非本地化 key | **已实施** | --- ## 二、本轮修复详情 ### TD-35 — `LocalizationManager.cs` 移除静态事件兼容层 **文件**:`Assets/Scripts/Localization/LocalizationManager.cs` **问题**: ```csharp // 旧实现:静态事件 + 显式接口桥接 public static event Action OnLanguageChanged; // ← 静态向后兼容 event Action ILocalizationService.OnLanguageChanged { add { OnLanguageChanged += value; } remove { OnLanguageChanged -= value; } } // SetLanguage 调用静态事件 OnLanguageChanged?.Invoke(language); ``` 这是典型的「兼容旧调用方」写法:`ILocalizationService.OnLanguageChanged` 的实例事件语义被静态事件偷换,导致: 1. 任何通过 `LocalizationManager.OnLanguageChanged +=` 直接订阅的代码绕过了接口,产生隐式静态依赖 2. 框架中不存在需要兼容的旧调用方,该层完全多余 3. 静态事件生命周期不受 MonoBehaviour Enable/Disable 控制,与 `CompositeDisposable` RAII 机制矛盾 **修复**: ```csharp // 新实现:纯实例事件 private event Action _onLanguageChanged; event Action ILocalizationService.OnLanguageChanged { add => _onLanguageChanged += value; remove => _onLanguageChanged -= value; } // SetLanguage 调用实例事件 _onLanguageChanged?.Invoke(language); ``` 同步更新文件顶部注释,移除「保持调用兼容」相关措辞,改为说明通过 `ILocalizationService` 接口订阅的正确用法。 --- ### Suggestion 1 → Fix — `DialogueUI.cs` 语音剪辑播放 **文件**:`Assets/Scripts/Dialogue/DialogueUI.cs` **问题**:`DialogueLine.voiceClip` 字段(`AudioClip`)已在数据层定义,`DialogueSequenceSO` 和 `DialogueLine` 都完整支持配置语音,但 `DialogueUI` 从未使用该字段,语音片段永远不会播放。 **修复**: ```csharp // 新增 Inspector 字段 [SerializeField] private AudioSource _voiceSource; // 语音播放源(可不配置) // ShowLine() — 头像赋值后追加: if (_voiceSource != null) { _voiceSource.Stop(); if (line.voiceClip != null) { _voiceSource.clip = line.voiceClip; _voiceSource.Play(); } } // SkipTyping() — StopCoroutine 后追加: _voiceSource?.Stop(); ``` `_voiceSource` 为可选配置(null 安全),`ShowLine` 先停止再播放(防止上一行语音未结束时重叠)。跳字时同步停止语音保持语音与文字的同步关系。 --- ### Suggestion 2 → Fix — `QuestObjectiveSO.cs` displayText 本地化 **文件**:`Assets/Scripts/Quest/QuestObjectiveSO.cs` **问题**: ```csharp [TextArea(1, 4)] public string displayText; // 直接文本,非本地化 key ``` 任务目标描述在运行时无法随语言切换更新,与框架本地化设计不符。 **修复**: ```csharp public string displayTextKey; // 本地化 key,对应 "Quest" 表中的条目 ``` 移除 `[TextArea]` 特性(key 通常是简短标识符,不需要多行编辑框)。Inspector 中填写如 `"obj_talk_elder"` 这样的 key,通过 `LocalizationManager.Get(displayTextKey, "Quest")` 在 UI 层取得显示文本。 注释也同步说明用途,使编辑器语义清晰。 --- ### Suggestion 3 → Fix — `ChallengeRoomManager.cs` SpawnPoint 空值警告 **文件**:`Assets/Scripts/Quest/ChallengeRoomManager.cs` **问题**: ```csharp Vector3 pos = entry.spawnPoint != null ? entry.spawnPoint.position : Vector3.zero; ``` 当 `spawnPoint` 未配置时静默回退到世界原点,策划不会收到任何提示,问题往往在运行时才被偶然发现。 **修复**: ```csharp Vector3 pos; if (entry.spawnPoint != null) { pos = entry.spawnPoint.position; } else { Debug.LogWarning($"[ChallengeRoomManager] encounter[{index}] 中的 enemyAddressKey='{entry.enemyAddressKey}' " + $"未配置 spawnPoint,将在 Vector3.zero 生成。请在 ChallengeRoomSO 中补全配置。", this); pos = Vector3.zero; } ``` 使用 `this` 作为第二参数,双击 Console 日志可直接定位到场景中的 `ChallengeRoomManager` 对象,加快排查效率。 --- ### Suggestion 4 → Fix — `LoadingScreenManager.cs` 提示文字本地化 **文件**:`Assets/Scripts/UI/LoadingScreenManager.cs` **问题**: ```csharp [SerializeField] private string[] _tipMessages; // 直接文字(非本地化 key) // ... _tipText.text = _tipMessages[Random.Range(0, _tipMessages.Length)]; ``` 注释已标注「P4-5 本地化模块完成后替换」,v15 已完整实现 Localization 模块,此 TODO 应立即落地。 **修复**: ```csharp // using BaseGames.Localization; 已添加到文件顶部 [SerializeField] private string[] _tipMessages; // 本地化 key(对应 "UI" 表中的条目,如 "tip_explore") // ... _tipText.text = LocalizationManager.Get(_tipMessages[Random.Range(0, _tipMessages.Length)], "UI"); ``` 类注释中移除「P4-5 本地化模块完成后替换」的 TODO 说明,类文档恢复简洁。 --- ## 三、修复汇总 | 编号 | 文件 | 问题类型 | 问题描述 | 修复方式 | |------|------|----------|----------|----------| | TD-35 | `Localization/LocalizationManager.cs` | 框架纯净性 | 静态事件 `OnLanguageChanged` 作为兼容层暴露,接口实现委托给静态事件,`SetLanguage` 调用静态事件;框架无旧调用方需兼容 | 移除静态事件,添加私有实例字段 `_onLanguageChanged`,接口实现直接包装实例字段,`SetLanguage` 调用实例事件 | | Fix-S1 | `Dialogue/DialogueUI.cs` | 功能缺失 | `DialogueLine.voiceClip` 字段从未被播放,语音功能形同虚设 | 新增 `[SerializeField] AudioSource _voiceSource`,`ShowLine` 中播放,`SkipTyping` 中停止 | | Fix-S2 | `Quest/QuestObjectiveSO.cs` | 数据一致性 | `displayText` 存储直接文本,不支持多语言,与框架本地化约定不符 | 重命名为 `displayTextKey`,移除 `[TextArea]`,通过 `LocalizationManager.Get(displayTextKey, "Quest")` 获取显示文本 | | Fix-S3 | `Quest/ChallengeRoomManager.cs` | 可调试性 | `spawnPoint` 为 null 时静默回退 `Vector3.zero`,策划配置遗漏不可见 | 拆分条件分支,null 分支增加 `Debug.LogWarning` 含 `this` context 对象 | | Fix-S4 | `UI/LoadingScreenManager.cs` | 数据一致性 | `_tipMessages` 存储直接字符串,随语言切换不刷新 | 添加 `using BaseGames.Localization`,用 `LocalizationManager.Get(key, "UI")` 包装取值,字段注释说明为本地化 key | --- ## 四、维度评分(更新) | 维度 | v15 得分 | v16 得分 | 变化原因 | |------|---------|---------|---------| | 架构设计 | 9.8 | 9.8 | 无变化 | | 性能优化 | 9.5 | 9.5 | 无变化 | | 可扩展性 | 9.6 | 9.6 | 无变化 | | 框架纯净性 | 9.7 | 9.9 | TD-35 彻底移除静态事件兼容层,接口实现完全规范 | | 编辑器友好 | 9.5 | 9.6 | Fix-S3 增加含 context 对象的 LogWarning,调试体验提升 | | 使用便利性 | 9.5 | 9.5 | 无变化 | | 数据逻辑一致性 | 9.6 | 9.8 | Fix-S2/S4 统一了 Quest 目标文本和 LoadingScreen 提示的本地化路径,消除数据层不一致 | **综合得分:9.68 / 10**(+0.12) --- ## 五、已知可接受的设计选择(更新) 原 v15 第五节条目 #2(`LocalizationManager.OnLanguageChanged` 静态事件)已升级为 TD-35 并修复,从本节移除。 | 编号 | 条目 | 状态 | |------|------|------| | ✅ 1 | `GlobalSFXPlayer` 单例 | 保持可接受,全局 SFX 便利性需要 | | ~~2~~ | ~~`LocalizationManager.OnLanguageChanged` 静态事件~~ | **TD-35 已修复,从此节移除** | | ✅ 3 | `SpellManager` SelfBuff/Summon/Teleport 分支未完整实现 | 保持可接受,设计预留扩展点 | | ✅ 4 | `SaveSlotController` 槽位数硬编码为 3 | 保持可接受,改动需协调多处 | | ✅ 5 | `EventChainManager` 帧合并 `_evaluatePending` | 保持可接受,刻意性能优化 | --- ## 六、后续建议(可择期执行) v15 的全部四条后续建议已在本轮实施,当前无新增后续建议。 框架在经历 16 个迭代后,核心代码已达到商业级 Action 游戏框架的一致性要求。后续工作重心建议转移至: 1. **运行时测试覆盖**:为核心系统(Combat、Quest、Localization)编写 Unity Test Framework 单元/集成测试,覆盖主要分支。 2. **Addressables 预加载策略文档化**:当前 `ChallengeRoomManager`、`SpawnManager` 均在运行时 `InstantiateAsync`,对于高频召唤场景可考虑预暖(Preload)标记的 Label 分组。 3. **Spell/StatusEffect 组合测试场景**:多个 StatusEffect 叠加时的优先级与互斥规则在代码层已有注释,建议在 Docs 层补充状态效果交互矩阵文档。 --- *上一版:[FrameworkReview_2026_May_v15.md](FrameworkReview_2026_May_v15.md)(加权 9.56)*