8.9 KiB
8.9 KiB
代码深度评审 DeepDive 2026 Q2
评审范围:
Assets/Scripts/416 个 .cs 文件(DeepDive_2026.md 修复完成后的新一轮评审)
重点模块:Audio、Camera、Dialogue、Quest、Progression、EventChain、UI、Tutorial、World(全子系统)、Support
评审标准:以商业级成熟作品(Hollow Knight、Celeste、Hades)为参照基线
一、评审维度与总分
| 维度 | 满分 | 得分 | 说明 |
|---|---|---|---|
| 架构设计 (Architecture) | 25 | 21 | SO 事件总线 + ServiceLocator 整体优秀,但仍有混用 Instance/ServiceLocator |
| 性能 (Performance) | 25 | 20 | 批处理 / 对象池 / 环形缓冲区均到位;3 处反射调用是负担 |
| 可扩展性 (Extensibility) | 20 | 17 | 谜题接口、EventChain、WorldStateRegistry 设计良好;对话变体未实装 |
| 编辑器友好 (Editor UX) | 15 | 13 | HurtBoxEditor / EventBusMonitorWindow 完善;极少数字段缺少 Tooltip |
| 使用便利性 (DX / API) | 15 | 12 | 核心 API 简洁;namespace 缺失与 SaveManager.Instance 残留拉低得分 |
| 合计 | 100 | 83 |
二、各维度详细评分与问题清单
2.1 架构设计 (21/25)
优点
- SO 事件频道体系成熟,
BaseEventChannelSO<T>+EventSubscription(IDisposable)+CompositeDisposable.AddTo()完整闭环,各系统无直接依赖。 - ServiceLocator 统一服务注册,
GetOrDefault<T>优雅处理可选服务。 - GameStateMachine 显式白名单状态机,避免非法转换,DeepDive_2026 已完善。
- 25 个 asmdef 严格单向依赖链(Core → Player → Combat → … → World),编译隔离优秀。
- WorldStateRegistry 作为 ScriptableObject,SO 注入代替全局单例,设计一流。
- EventChainManager 中继 C# 事件架构清晰,Editor 专用静态事件零运行时开销。
问题
| ID | 文件 | 描述 | 严重性 |
|---|---|---|---|
| A-1 | QuestManager.cs |
OnEnable/OnDisable 使用已废弃的 SaveManager.Instance?.Register/Unregister |
中 |
| A-2 | ShopController.cs |
同 A-1,SaveManager.Instance?.Register/Unregister |
中 |
| A-3 | EventChainManager.cs |
Awake 中使用 SaveManager.Instance?.GetCompletedChains() |
中 |
| A-4 | TutorialManager.cs |
使用原始 Singleton 模式(public static Instance),未注册 ServiceLocator |
低 |
| A-5 | DialogueManager.cs |
ResolveVariant() 为存根,变体条件从未与 WorldStateRegistry 实际对接 |
低 |
2.2 性能 (20/25)
优点
EventBusMonitor:固定 256 条EventRecord[]环形缓冲区,Editor 零 GC。GlobalObjectPool:WarmupAsync预热,IObjectPoolService接口注册,ServiceLocator解耦。BatchLOSSystem:HashSet + List双结构,O(1) 查重 + 分帧限制 8 个请求。EquipmentManager.UsedNotches:缓存字段,完全消除 LINQ Sum。AudioManager:6 路 SFX 轮转池,避免高密度战斗音效互戳;BGM 双 Source 交叉淡入淡出。HomingProjectile:方向向量用sqrMagnitude比较速度上限,无开方开销。
问题
| ID | 文件 | 描述 | 严重性 |
|---|---|---|---|
| P-1 | LiquidZone.cs |
PlayFeedback() 通过 GetMethod("PlayFeedbacks") 反射调用,每次玩家进出液体区都触发 |
高 |
| P-2 | PuzzleSwitch.cs |
同 P-1,同一反射模式 | 高 |
| P-3 | PuzzleReceiver.cs |
同 P-1,同一反射模式 | 高 |
| P-4 | Projectile.cs |
ReturnToPool() 使用 GlobalObjectPool.Instance(已标废弃),应走 ServiceLocator |
低 |
2.3 可扩展性 (17/20)
优点
PuzzleInterfaces.cs:ISwitchable / IMovable / IActivatable三接口,谜题元素完全可替换实现。AchievementCondition+ 11 个具体实现:抽象基类 + 工厂,新增成就类型零侵入现有代码。EventChainSO+ChainCondition:ScriptableObject 配置驱动,策划可在 Inspector 搭建复杂叙事链。WorldStateRegistry.WorldObjectCategory:枚举泛化 API,一个 registry 统一 Collectible/Door/Flag 等全部世界状态。ToolSlotManager:工具栏插槽与 SO 解耦,扩展新工具只需新建 ToolSO。
问题
| ID | 文件 | 描述 | 严重性 |
|---|---|---|---|
| E-1 | DialogueManager.cs |
ResolveVariant 永远返回原始序列;条件分支对话完全无效 |
中 |
| E-2 | AchievementManager.cs |
注册为具体类型 Register<AchievementManager>,无法在测试中 mock |
低 |
2.4 编辑器友好 (13/15)
优点
HurtBoxEditor:运行时实时展示注入对象,调试受击逻辑极为便利。EventBusMonitorWindow:环形缓冲区在 Editor 窗口可视,零 GC。BossSkillSequenceWindow:Boss 技能序列可视化 Inspector。AddressKeyValidator:构建时自动校验 Addressable Key 拼写,防止发布错误。WorldStateRegistry.OnEnable() => _states.Clear():每次进入 Play Mode 自动重置 SO 状态,无脏数据问题。EventChainManager:OnChainExecutedInEditor静态事件仅在编辑器存在,零运行时影响。
问题
| ID | 文件 | 描述 | 严重性 |
|---|---|---|---|
| UX-1 | 全局 | SpeedrunTimer、AccessibilityManager 缺少 namespace,IDE 浏览/搜索时易混淆 |
低 |
| UX-2 | AntiSoftlockSystem.cs |
类体未在 namespace 内缩进,与项目其他文件风格不一致 | 低 |
2.5 使用便利性(API/DX)(12/15)
优点
ServiceLocator.GetOrDefault<T>(fallback)安全查询,代码调用方无需 null 判断。DamageInfo.From(so, knockDir, sourcePos, layer)工厂方法 +Builder:两种构造路径清晰。CompositeDisposable.AddTo():Rx 风格订阅管理,防止事件泄漏。QuestManager:IQuestManager接口 +ServiceLocator.Register<IQuestManager>(this),调用方面向接口编程。SkillSlotNames:常量类消除"SoulSkill"等魔法字符串。
问题
| ID | 文件 | 描述 | 严重性 |
|---|---|---|---|
| D-1 | SpeedrunTimer.cs |
缺少 namespace,与全局命名空间污染冲突 | 低 |
| D-2 | AccessibilityManager.cs |
缺少 namespace | 低 |
| D-3 | UIManager.cs |
TogglePause() 只开不合,按两次暂停后第二次无效 |
中 |
| D-4 | AchievementManager.cs |
_saveRef 字段在 OnLoad 赋值后从未读取(死代码) |
低 |
三、与商业标准的横向对比
| 对比项 | Hollow Knight 参照模式 | 本项目现状 |
|---|---|---|
| 事件解耦 | 自定义 C# 事件 + delegate | SO 频道体系,更 Inspector 友好 ✓ |
| 状态机 | 纯 C# 非 MonoBehaviour 状态 | 同,PlayerStateBase 模式一致 ✓ |
| 对象池 | 静态池,键值 string | GlobalObjectPool + IObjectPoolService + ServiceLocator ✓✓ |
| 存档系统 | PlayerPrefs + 二进制 | Newtonsoft.Json + MD5 校验 + SemaphoreSlim 防并发 ✓✓ |
| 依赖注入 | 无(大量 singleton) | ServiceLocator(部分残留 singleton 待清理) ≈ |
| Addressables | Unity 自有 AssetBundle | Addressables + AddressKeyValidator 构建校验 ✓✓ |
| 谜题系统 | 基于碰撞的单一谜题 | ISwitchable/IActivatable 接口体系 ✓✓ |
| 对话变体 | 全局标记条件分支 | 框架存在但未接入 WorldStateRegistry(TODO) △ |
| 速通支持 | 无内置计时器 | SpeedrunTimer + ISaveable 持久化 ✓ |
| 无障碍 | 无内置色盲/震屏控制 | AccessibilityManager + ColorblindFilter ✓ |
四、修复项汇总(按优先级)
| 优先级 | ID | 文件 | 修复内容 |
|---|---|---|---|
| 高 | P-1,P-2,P-3 | LiquidZone.cs, PuzzleSwitch.cs, PuzzleReceiver.cs |
反射 → MoreMountains.Feedbacks.MMFeedbacks 直接转型 |
| 中 | A-1 | QuestManager.cs |
SaveManager.Instance → ServiceLocator.GetOrDefault<SaveManager>() |
| 中 | A-2 | ShopController.cs |
同上 |
| 中 | A-3 | EventChainManager.cs |
同上 |
| 中 | D-3 | UIManager.cs |
TogglePause() 改为真实 toggle |
| 低 | P-4 | Projectile.cs |
GlobalObjectPool.Instance → ServiceLocator.GetOrDefault<IObjectPoolService>() |
| 低 | D-1 | SpeedrunTimer.cs |
补齐 namespace |
| 低 | D-2 | AccessibilityManager.cs |
补齐 namespace |
| 低 | D-4 | AchievementManager.cs |
删除 _saveRef 死代码字段 |
| 低 | UX-2 | AntiSoftlockSystem.cs |
类体补全缩进 |
五、不在本次修复范围内的已知问题(技术债)
| 问题 | 原因/说明 |
|---|---|
DialogueManager.ResolveVariant() 存根 |
需 Phase 4 完整接入 WorldStateRegistry 条件标记,属功能开发范畴,非代码质量问题 |
TutorialManager raw singleton |
功能正确,仅可测试性不足;整改需新增接口,成本大于收益 |
AchievementManager 注册具体类型 |
暂无 mock 需求,改为接口注册需新建 interface,暂缓 |
AudioManager.PlayBGM/PlaySFX(key) 存根 |
等待 Phase 2 AudioEventSO 模块,属功能开发范畴 |