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

17 KiB
Raw Blame History

BaseGames 框架代码评审 v15

评审日期2026-05 (会话 15
前置版本v14得分 9.52/10修复 TD-21TD-29
本次覆盖模块Input 系统、Animation 事件系统、Parry 弹反、Dialogue 对话、Quest/Challenge 任务与挑战、Feedback 反馈、Spells 法术、EventChain 事件链、Cutscene 过场、Localization 本地化、UI 全模块HUD / Menus / Settings
发现问题TD-30 TD-34共 5 项,全部已修复)
修复后得分9.56 / 10


一、综合概述

本轮覆盖了框架剩余全部模块,完成对约 270+ 个 C# 文件的整体阅读。框架整体架构成熟,各子系统在 SO 事件总线、ServiceLocator、CompositeDisposable RAII、ISaveable 四大支柱上高度统一代码风格、命名规范和性能意识对象池、StringBuilder、零分配 TMP API均处于商业级水准。本轮新发现的 5 个问题集中在「框架纯洁性保障」与「现有约定遵守」两类,与框架设计原则无根本冲突,修复后可进一步强化框架一致性。


二、各模块评审

2.1 Input 系统

文件 评分 说明
InputReaderSO.cs ★★★★★ 单一 SO 封装全部 InputActionEnableGameplayInput/EnableUIInput/DisableAllInput 明确;LoadBindingOverrides/SaveBindingOverrides 通过 PlayerPrefs 完整落地
InputBuffer.cs ★★★★★ 命名字段处理跳跃/攻击/冲刺缓冲;Consume*() 读取并清零,无泄漏;Mathf.Max(0f, timer-dt) 防负值
ConflictDetector.cs ★★★★★ HashSet<string> 分组按 effectivePath正确跳过复合绑定父节点
InputReaderBootstrap.cs ★★★★☆→★★★★★ TD-30 已修复,移除 Resources.FindObjectsOfTypeAll 名称回退,改为 AwakeDebug.Assert 强制 Inspector 赋值

TD-30 详情

  • 位置Assets/Scripts/Input/InputReaderBootstrap.cs
  • 问题OnEnable_inputReader == null 时调用 Resources.FindObjectsOfTypeAll<InputReaderSO>() 并按名称 "InputReader" 搜索 —— 违反「框架不依赖运行时查找资产」原则,名称拼写变更即静默失败。
  • 修复:删除整个 FindDefaultInputReader() 方法及 OnEnable 中的条件分支;在 Awake 中加入 Debug.Assert 强制 Inspector 赋值;Start 直接使用 _inputReader(空时 early-return

2.2 Animation 事件系统

文件 评分 说明
AnimationEventBinder.cs ★★★★★ 静态工具类,循环捕获变量避免闭包陷阱;ClipTransition.Events.Add(normalizedTime, Action) 正确
AnimationEventConfigSO.cs ★★★★★ SortedEvents LINQ 在 Awake 中排序,不在热路径执行;GetNormalizedTime 小 N 线性查找合理;ExpectedClipLength [HideInInspector] 防止编辑器漂移
PlayerAnimationEvents.cs ★★★★★ GetComponentInParent<IFeedbackPlayer>() ?? NullFeedbackPlayer.Instance 空对象模式HandleEvent switch 覆盖 HitBox/HurtBox/Parry/Feedback/SFX 全路径
EnemyAnimationEvents.cs ★★★★★ 与玩家对称SpawnProjectile/RoarStart/PhaseTwoStart 完整
AnimationEventType.cs ★★★★★ 枚举 + 无状态设计,纯数据
IAnimationEventHandler.cs ★★★★★ 接口单一职责

2.3 Parry 弹反系统

文件 评分 说明
ParryConfigSO.cs ★★★★★ 全部时序参数集中含完美弹反阈值、子弹时间、灵力奖励Inspector 标注友好
ParrySystem.cs ★★★★★ 状态机 Inactive→Startup→Active→EndLag→CounterWindowunscaledDeltaTime 保证子弹时间期间冷却正常计时;ConsumeParry() 单次原子消费C# 事件 OnParryConsumed 供 PlayerController 订阅SO 事件 _onParrySuccess 供 UI/特效;完美弹反子弹时间通过协程实现,TimeScale 复原安全
ParryInfo.cs ★★★★★ 轻量 struct 负载,含 IsPerfect / SoulGained
ParryInfoEventChannelSO.cs ★★★★★ 与框架事件总线统一

2.4 Dialogue 对话系统

文件 评分 说明
DialogueDataSO.cs ★★★★☆ 简洁 SOplaceholderText 字段意义略模糊,可考虑 XML doc 补充
DialogueSequenceSO.cs ★★★★★ DialogueLine struct 含 speakerNameKey/textKey/portraitSprite/voiceClipConditionalVariant[] 支持 WorldState 分支;不可变数据清晰
DialogueUI.cs ★★★★☆→★★★★★ TD-31 已修复TypeLineStringBuilder + TMP.SetText(sb) 零分配正确;WaitForSecondsRealtime 防暂停
InteractableNPC.cs ★★★★★ 模板方法模式;ServiceLocator.GetOrDefault<IDialogueService>() 解耦
DialogueManager.cs ★★★★★ 重复守卫 + ServiceLocator 注册;EnableUIInput 切换 ActionMapPlaySequence 协程管理行推进与跳过
NarrativeNPC.cs ★★★★★ 继承 InteractableNPC覆盖 GetCurrentDialogue 返回固定序列

TD-31 详情

  • 位置Assets/Scripts/Dialogue/DialogueUI.cs
  • 问题ShowLine() 直接将 line.speakerNameKey 赋给 _speakerNameText.textSkipTyping() 直接将 _currentLine.textKey 赋给 _dialogueText.textTypeLine 也直接使用 line.textKey 作为显示文本。三处均绕过本地化管道,导致玩家看到的是本地化 key 而非翻译后文本。
  • 修复:引入 using BaseGames.Localization;,三处改为 LocalizationManager.Get(key, "Dialogue"),静态 Facade 在服务未注册时直接返回 key保证向后安全。

2.5 Quest & Challenge 任务与挑战

文件 评分 说明
QuestSO.cs ★★★★★ 含 objectives/prerequisiteQuestIds/minAffinity/reward/canFail/branchesQuestBranch 条件分支结构清晰
QuestObjectiveSO.cs ★★★★★ 抽象 SO + 5 种内置实现TalkToNPC/Defeat/Collect/Reach/UseSkill多态无需 if/elseQuestObjectiveState 运行时状态分离,不污染 SO
QuestManager.cs ★★★★★ _questIndex 字典 O(1) 查找事件驱动进度追踪EnemyDied/CollectiblePickup/SceneLoaded/NpcDialogueISaveable 完整 OnSave/OnLoad分支解锁逻辑清晰
QuestGiver.cs ★★★★★ 模板方法覆盖 Interact_InternalGetCurrentDialogueswitch 表达式选对话版本;GetComponentInParent<PlayerStats>() 避免直接依赖 PlayerController
RewardSO.cs ★★★★☆ Apply(IRewardTarget) 策略模式具体奖励类型Geo/Ability/Item子类扩展性良好
ChallengeRoomManager.cs ★★★★★ 自动快速存档防软死锁;时间限制 + requireNoHit 挑战检测;逐波生成逻辑
ChallengeRoomSO.cs ★★★★★ SO 纯数据定义波次与条件
BossRushSequenceSO.cs ★★★★★ 顺序关卡 SO 序列

2.6 Feedback 反馈系统

文件 评分 说明
IFeedbackPlayer.cs ★★★★★ 接口语义完整PlayHit/PlayParrySuccess/TakeHit/Death/Heal/LandImpact/AttackWhoosh/JumpLaunch/Footstep/TriggerPreset/PlaySFXById
FeedbackConfigSO.cs ★★★★★ 轻量全局配置,含闪白颜色/时长;[Min(0.01f)] 保证有效范围
PlayerFeedback.cs ★★★★★ MMF_Player 字段分组Awake 中 BuildMap 构建预设字典switch 表达式 HitWeight → player未找到预设时 Debug.LogWarning 而非静默失败
NullFeedbackPlayer.cs ★★★★★ 空对象模式,所有方法空实现,Instance 单例仅限内部框架用

2.7 Spells 法术系统

文件 评分 说明
SpellSO.cs ★★★★★ 五种 SpellEffectType投射/AoE/Buff/召唤/瞬移各字段分组清晰;displayNameKey/descriptionKey 本地化友好
SpellManager.cs ★★★★★ OnEnable/OnDisable 订阅 SpellCastEventUpdate 冷却递减;CooldownFraction 属性供 UI 使用;ExecuteSpellEffect 目前实现投射物/AoE 生成SelfBuff/Summon/Teleport 预留扩展点

2.8 EventChain 世界事件链

文件 评分 说明
EventChainSO.cs ★★★★★ ChainCondition 抽象基类 + ResetState() 防 SO 跨 PlayMode 状态残留;BossDefeatedCondition/FlagSetCondition/AbilityUnlockedCondition 内置实现完整
EventChainManager.cs ★★★★★ 中继 C# 事件供 Condition 订阅;_evaluatePending 帧合并模式(多事件同帧仅执行一次 DoEvaluateAllExecuteChain 防重入;#if UNITY_EDITOR 编辑器日志事件零运行时开销

2.9 Cutscene 过场系统

文件 评分 说明
CutsceneSO.cs ★★★★★ Timeline 资产 + CutsceneBinding 数组解耦场景对象引用BlendIn/BlendOut 摄像机配置DialogueLayers 可叠加对话
CutsceneManager.cs ★★★★★ PlayableDirector 包装Track 绑定循环;_onPlayCutsceneById 事件驱动;onCompleted 回调用于存档 flagplayOnlyOnce 标记存档去重
CutsceneTrigger.cs ★★★★★ Collider2D 触发播放,isSkippable 尊重 SO 配置
SignalEmitterClip.cs ★★★★★ Timeline 信号到 SO 事件频道的桥接,零场景对象硬引用

2.10 Localization 本地化

文件 评分 说明
LocalizationManager.cs ★★★★★ 双层缓存language/table → dict回退链当前语言 → English → key静态 Facade Get(key, table) 保持调用兼容;ILocalizationService.OnLanguageChanged 双向代理静态/实例事件ISaveable 持久化语言选择到 SaveData.Settings
Language.cs ★★★★★ 枚举定义干净
LanguageEventChannelSO.cs ★★★★★ 与框架事件总线统一

2.11 UI 系统

2.11.1 HUD

文件 评分 说明
HUDController.cs ★★★★★ 全事件驱动HP/Soul/Spirit/Geo/Spring/Form/InteractPromptHP Cell 复用策略(复用 + SetActive不 Destroy/重建);CompositeDisposable _subs RAII 订阅
BossHPBar.cs ★★★★★ 默认隐藏Boss 战开始时协程滑入;阶段标记点 Prefab 动态生成;WaitForSecondsRealtime 不受时间缩放影响
FloatingDamageText.cs ★★★★★ 对象池驱动;RectTransformUtility.ScreenPointToLocalPointInRectangle 适配 Overlay/Camera/WorldSpace 三种 Canvas 模式;不在 Awake 缓存 Camera.main 防过场主摄像机切换导致引用过期

2.11.2 Menus

文件 评分 说明
PauseMenuController.cs ★★★★★ 按钮绑定在 Awake 集中,_uiManager.CloseTopPanel() 利用栈管理GO_TO_MAIN_MENU 走 SceneLoadRequest 事件通道,不直接 SceneManager
DeathScreenController.cs ★★★★★ OnEnable 启动延迟协程1.5s 缓冲);OnDisable StopAllCoroutines 防对象池复用异常
SaveSlotController.cs ★★★★☆→★★★★★ TD-34 已修复GetSlotSummaryAsync + LoadAsync 异步友好;槽位数硬编码为 3 可通过常量改善(小优化)

2.11.3 Settings

文件 评分 说明
RebindPanel.cs ★★★★★ 排他锁设计(同时只允许一行重绑定);完成后自动 SaveBindingOverrides()ResetAll 恢复默认并刷新所有行
RebindActionRow.cs ★★★★★ InputActionRebindingExtensions.PerformInteractiveRebinding 正确;冲突高亮刷新
SettingsPanelController.cs ★★★★★ Tab 切换 + 应用/重置逻辑;通过 ServiceLocator 获取 ILocalizationService

2.11.4 通用 UI

文件 评分 说明
UIManager.cs ★★★★★ Stack<GameObject> 实现面板历史;OpenPanel/CloseTopPanel 语义清晰;事件驱动 GameStateId 控制 HUD 显隐
LoadingScreenManager.cs ★★★★☆→★★★★★ TD-32 已修复_minDisplayTime 防闪屏;随机背景/提示文字
ToastManager.cs ★★★★★ Queue<ToastData> 串行显示;CanvasGroup 淡入淡出;WaitForSecondsRealtime 防暂停跳帧
SaveIndicator.cs ★★★★★ 存档图标淡入淡出,订阅 _onSaveBegan/Completed 事件频道
InputDeviceIconSwitcher.cs ★★★★☆→★★★★★ TD-33 已修复InputDeviceIconSetSO 静态 Current 属性;InputIconImage 自注册 Start() 刷新

三、本轮修复汇总

编号 文件 问题描述 修复方式
TD-30 Input/InputReaderBootstrap.cs OnEnableResources.FindObjectsOfTypeAll<InputReaderSO>() 按名称搜索回退,违反框架纯净性原则,名称改动即静默失败 删除 FindDefaultInputReader() 及条件分支;改为 AwakeDebug.Assert 强制 Inspector 赋值
TD-31 Dialogue/DialogueUI.cs ShowLine()line.speakerNameKey 直接赋显示文本;SkipTyping()TypeLineline.textKey 直接显示,绕过本地化管道 三处改为 LocalizationManager.Get(key, "Dialogue") 静态 Facade 调用
TD-32 UI/LoadingScreenManager.cs OnEnable/OnDisableOnEventRaised +=/-= 直接订阅,不符合框架 .Subscribe().AddTo(_subs) RAII 约定 新增 CompositeDisposable _subs,改为标准 Subscribe 模式
TD-33 UI/InputDeviceIconSwitcher.cs SwitchIconSet 调用 GetComponentsInChildren 仅遍历自身子树,分散在其他 Canvas 区域的 InputIconImage 组件不会刷新 改为 FindObjectsByType<InputIconImage>(FindObjectsInactive.Include, FindObjectsSortMode.None) 全场景刷新
TD-34 UI/Menus/SaveSlotController.cs OnEnable 声明为 async voidRefreshAsync() 抛出异常时会被 Unity SynchronizationContext 吞掉或导致未处理异常崩溃 改为同步 OnEnable,通过 Task.ContinueWith + Debug.LogException 在主线程捕获并记录异常

四、维度评分(更新)

维度 v14 得分 v15 得分 说明
架构设计 9.8 9.8 SO 事件总线 + ServiceLocator + AssemblyDef 依赖图依然优秀
性能优化 9.5 9.5 StringBuilder/TMP 零分配、帧合并评估、对象池完整
可扩展性 9.6 9.6 多态 SO 策略ChainCondition/QuestObjective/SpellEffect零代码新增类型
框架纯净性 9.3 9.7 TD-30/TD-31/TD-32 修复后,资产查找回退/本地化绕过/订阅模式偏差全部消除
编辑器友好 9.5 9.5 Header 分组/Tooltip/CreateAssetMenu 全覆盖
使用便利性 9.4 9.5 TD-33/TD-34 修复后图标刷新覆盖完整async 异常可见
数据逻辑一致性 9.6 9.6 ISaveable/IQuestManager/ILocalizationService 注册注销对称

综合得分9.56 / 10+0.04


五、已知可接受的设计选择(非问题)

以下条目在讨论后确认为刻意的设计决策,不计入扣分:

  1. GlobalSFXPlayer 单例:全局 SFX 播放的便利需要,不影响核心数据流。
  2. LocalizationManager.OnLanguageChanged 静态事件:为保持旧调用方兼容而保留,已通过显式接口实现与实例事件统一。
  3. SpellManager.ExecuteSpellEffect SelfBuff/Summon/Teleport 分支未完整实现:设计预留扩展点,当前版本仅 Projectile/AoE 已上线。
  4. SaveSlotController 槽位数硬编码为 3:与存档系统约定一致,改动需协调多处,当前可接受。
  5. EventChainManager 帧合并 _evaluatePending:多事件同帧仅触发一次 DoEvaluateAll,是刻意的性能优化,不是遗漏。

六、后续建议(非必要,可择期执行)

  1. Dialogue 语音剪辑播放DialogueLine.voiceClip 字段已在 SO 中定义,但 DialogueUI.TypeLine 目前尚未触发播放,可在 ShowLine 开头通过 IFeedbackPlayer.PlaySFXById 或 AudioSource 播放。
  2. QuestObjectiveSO.displayText 本地化:当前为直接文本,建议改为 displayTextKey 并通过 LocalizationManager.Get 获取,与对话系统保持一致。
  3. ChallengeRoomManager 敌人生成 SpawnPoint 为 null 时回退到 Vector3.zero:逻辑正确但缺少 Debug.LogWarning 提示策划配置遗漏,可加一行日志。
  4. LoadingScreenManager _tipMessages 本地化注释已标注「P4-5 本地化模块完成后替换」,本次 Localization 模块已完成,可统一替换为 key 驱动。