Files
zeling_v2/Docs/Review/FrameworkReview_2026_May_v16.md
2026-05-13 09:19:54 +08:00

9.9 KiB
Raw Blame History

BaseGames 框架代码评审 v16

评审日期2026-05会话 16
前置版本v15得分 9.56/10修复 TD-30TD-34
本次变更性质:针对 v15「已知可接受的设计选择」与「后续建议」的全量落地修复
发现问题TD-35共 1 项)+ Suggestion 14共 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

问题

// 旧实现:静态事件 + 显式接口桥接
public static event Action<Language> OnLanguageChanged;  // ← 静态向后兼容

event Action<Language> ILocalizationService.OnLanguageChanged
{
    add    { OnLanguageChanged += value; }
    remove { OnLanguageChanged -= value; }
}

// SetLanguage 调用静态事件
OnLanguageChanged?.Invoke(language);

这是典型的「兼容旧调用方」写法:ILocalizationService.OnLanguageChanged 的实例事件语义被静态事件偷换,导致:

  1. 任何通过 LocalizationManager.OnLanguageChanged += 直接订阅的代码绕过了接口,产生隐式静态依赖
  2. 框架中不存在需要兼容的旧调用方,该层完全多余
  3. 静态事件生命周期不受 MonoBehaviour Enable/Disable 控制,与 CompositeDisposable RAII 机制矛盾

修复

// 新实现:纯实例事件
private event Action<Language> _onLanguageChanged;
event Action<Language> 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)已在数据层定义,DialogueSequenceSODialogueLine 都完整支持配置语音,但 DialogueUI 从未使用该字段,语音片段永远不会播放。

修复

// 新增 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

问题

[TextArea(1, 4)]
public string displayText;   // 直接文本,非本地化 key

任务目标描述在运行时无法随语言切换更新,与框架本地化设计不符。

修复

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

问题

Vector3 pos = entry.spawnPoint != null ? entry.spawnPoint.position : Vector3.zero;

spawnPoint 未配置时静默回退到世界原点,策划不会收到任何提示,问题往往在运行时才被偶然发现。

修复

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

问题

[SerializeField] private string[] _tipMessages;   // 直接文字(非本地化 key
// ...
_tipText.text = _tipMessages[Random.Range(0, _tipMessages.Length)];

注释已标注「P4-5 本地化模块完成后替换」v15 已完整实现 Localization 模块,此 TODO 应立即落地。

修复

// 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 _voiceSourceShowLine 中播放,SkipTyping 中停止
Fix-S2 Quest/QuestObjectiveSO.cs 数据一致性 displayText 存储直接文本,不支持多语言,与框架本地化约定不符 重命名为 displayTextKey,移除 [TextArea],通过 LocalizationManager.Get(displayTextKey, "Quest") 获取显示文本
Fix-S3 Quest/ChallengeRoomManager.cs 可调试性 spawnPoint 为 null 时静默回退 Vector3.zero,策划配置遗漏不可见 拆分条件分支null 分支增加 Debug.LogWarningthis 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 第五节条目 #2LocalizationManager.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 预加载策略文档化:当前 ChallengeRoomManagerSpawnManager 均在运行时 InstantiateAsync对于高频召唤场景可考虑预暖Preload标记的 Label 分组。
  3. Spell/StatusEffect 组合测试场景:多个 StatusEffect 叠加时的优先级与互斥规则在代码层已有注释,建议在 Docs 层补充状态效果交互矩阵文档。

上一版:FrameworkReview_2026_May_v15.md(加权 9.56