Files
zeling_v2/Docs/Review/FrameworkReview_2026_May_v10.md

19 KiB
Raw Blame History

Zeling v2 框架全量代码评审报告 v10

评审时间2026 年 5 月
评审范围Assets/Scripts/ 所有 .cs 文件(~350 个)
前置版本v1-v9 评审报告(综合评分 9.08/10
本版改进:全量批量读取剩余 ~200 个未覆盖文件,完整覆盖所有模块


一、综合评分总览

维度 v9 评分 v10 评分 变化 说明
架构设计 9.0 9.2 全量审查后发现 FSM、存档迁移链等更多亮点
性能 9.1 9.1 BatchLOS、WFS 缓存优秀,但发现平台移动等遗漏点
可扩展性 9.2 9.3 SaveData DLC/ExtensionData/NGPlus 设计值得加分
编辑器友好 9.3 9.4 AddressKeyValidator 构建钩子发现,编辑器工具链完整
使用便利性 8.8 9.0 InputBuffer/SpeedrunTimer/RebindPanel 等 API 设计良好
综合 9.08 9.20 全量审查后整体印象进一步提升

结论Zeling v2 框架在商业 2D 动作 RPG 标准下,已达到高度成熟的生产级水平。核心系统(存档、输入、状态机、对象池、批量 LOS均具备商业游戏质量。发现 6 个可修复问题,无架构级缺陷。


二、各层模块详细评审

2.1 Core 层 — 基础骨架

GameStateMachine★★★★★

纯 C#(非 MonoBehaviour状态机设计无懈可击

  • ValidNextStates 白名单校验防止任意跳转,转换安全性有保障
  • Dictionary<GameStateId, IGameState> O(1) 查找
  • OnEnter/OnExit/Tick 生命周期严格分离
  • GameManager_prePauseState 保存/恢复机制处理了暂停状态的 re-entry 语义

典型代码GameManager.cs

// 暂停前保存当前状态Resume 时精确恢复
_prePauseState = _stateMachine.CurrentStateId;
_stateMachine.TransitionTo(GameStateId.Paused);

SaveManager + LocalFileStorage★★★★★

存档系统是本框架最亮眼的实现之一:

  • SemaphoreSlim(1,1) 序列化异步存档/读档,防止并发写入腐化
  • HMAC-SHA256 完整性校验(先清零再算再写,两次序列化但安全性无妥协)
  • LocalFileStorage 原子写入链:.tmp → File.Replace → .bak,任何阶段崩溃均可恢复
  • RunFireAndForget 包装 fire-and-forget异常不会 unobserved 静默吞掉
  • SlotSummary API 让 SaveSlotController 无需完整反序列化即可读取摘要

SaveData★★★★★

SaveData 数据结构的前向兼容设计极为成熟:

[JsonExtensionData]
public Dictionary<string, JToken> ExtensionData = new();  // 未知字段保留
public Dictionary<string, JObject> DLC = new();           // DLC 扩展节点
public NGPlusSaveData NGPlus = null;                       // null = 非 NG+ 模式
  • [JsonExtensionData] 保证新版游戏读取旧存档时不丢弃未知字段
  • DLC 字典为未来付费内容提供零侵入的扩展点

SaveMigrator★★★★☆

版本迁移链fall-through 语义)规范优雅:

if (IsOlderThan(v, "2.0")) { /* 补充 2.0 新字段 */ v = "2.0"; }
if (v == "2.0")             { /* 补充 2.1 新字段 */ v = "2.1"; }

评价:每次版本升级只需追加一个 if 块,无需修改旧逻辑,迁移安全。

GameServiceRegistrar★★★★★

DefaultExecutionOrder(-2000) 最早执行,服务注册器设计合理:

  • AudioListener 去重双路径Inspector 预绑定快路径vs 运行时扫描(兜底)
  • RegisterIfAbsent + NullAudioService/NullPlatformService 在框架内唯一合理的零侵入 Null Object 兜底

DifficultyManager★★★★★

SteelSoul 模式的单向锁定逻辑体现了对业务规则的精准建模:

if (CurrentLevel == DifficultyLevel.SteelSoul && level != DifficultyLevel.SteelSoul)
{
    Debug.LogWarning("[DifficultyManager] SteelSoul 模式无法在游戏中途降级。");
    return;
}

ISaveable + IDifficultyService 双接口实现,存档/服务解耦。


2.2 Input 层

InputReaderSO★★★★☆

  • OnEnable 重置所有缓存状态Domain Reload 安全
  • 完整的重绑定 APIStartRebinding/SaveBindingOverrides/LoadBindingOverrides/ResetBindings
  • HandlePause 中的 FindPauseChannelByName() 回退是已知技术债(见 §三 TD-06

InputBuffer★★★★★

帧级输入缓冲实现简洁到位:

  • 具名 handler 方法(HandleJumpStarted 等)保证 OnEnable/OnDisable 可以对称移除委托
  • ConsumeXxx() 模式(读取并清零)避免重复触发
  • 三路独立缓冲时长Jump 0.15s / Attack 0.12s / Dash 0.10s)针对手感调优

2.3 Audio 层

AudioManager★★★★★

  • 双 AudioSource BGM 交叉淡入淡出coroutine-based非线性插值
  • SFX 轮转池(_sfxRoundRobin)避免同帧叠音 + GC
  • BuildSFXLookup 构建 Dictionary<string, AudioEventSO>O(1) 查找
  • Initialize()ISettingsService 拉取四路音量,初始化时序明确

BGMController★★★★★

BGM 状态机Exploration/Boss/Victory/None事件驱动无轮询。每个 Boss 区域通过事件频道切换 BGM与战斗逻辑完全解耦。


2.4 Player 层

PlayerStats★★★★★

  • CompositeDisposable 订阅难度变更事件,OnEnable/OnDisable 对称
  • 护符修改器双 Dictionaryflat/percent支持叠加计算
  • IsInvincible / IsAlive 属性封装,外部只读

FormController★★★★★

三形态切换的三层通知设计清晰:

  1. SO 事件广播索引UI/Save
  2. C# 事件WeaponManager 订阅)
  3. SkillHUD 刷新事件

架构文档对应 05_PlayerModule §6,意图清晰。

InputBuffer — 已在 §2.2 评审。


2.5 Combat 层

ClashResolver★★★★★

拼刀系统去重方案精巧:

(int, int) key = (Math.Min(idA, idB), Math.Max(idA, idB));
if (!_processedThisFrame.Add(key)) return;

使用有序元组作为 HashSet key每帧 LateUpdate 清空,防止同帧双方 HitBox 各触发一次的重复处理。比 XOR 哈希更安全(无碰撞风险)。

ParrySystem★★★★★

相位 FSMInactive→Startup→Active→EndLag→CounterWindow使弹反逻辑透明可调

public bool IsParrying         => _phase == ParryPhase.Active;
public bool IsInCounterWindow  => _phase == ParryPhase.CounterWindow;

C# 事件 OnParryActivated/OnParryConsumed 解耦 VFX/Audio 响应。

StatusEffectManager★★★★★

List + Dict 双结构的工程决策有理有据:

  • ListUpdate 遍历无额外查找
  • DictO(1) 按类型查找(是否已有同类 effect
  • RegisterEffectFactory运行时可扩展Mod/DLC 友好)

2.6 Enemies 层

BatchLOSSystem★★★★★

Unity 2022.3 中 2D Raycast Job 未稳定,该实现以每帧限额轮询代替 Job System是正确的降级策略

  • Swap-and-pop O(1) UnregisterEnemyQuotaManager 一致的模式)
  • _indexMap 维护每个注册者的数组下标
  • _maxRequestersPerFrame 可配置,大规模场景可调优

BossSkillExecutor★★★★★

WaitForSeconds 静态缓存 + [RuntimeInitializeOnLoadMethod] 保证 Play Mode 重启时缓存清空:

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ClearCache() => s_wfsCache.Clear();

InterruptCurrentSkill 安全终止协程并复位状态。


2.7 World 层

WorldStateRegistry★★★★★

  • OnEnable → _states.Clear() 保证 Domain Reload 安全SO 在编辑器重新进入 Play Mode 时不保留旧数据)
  • 泛化 Mark<TCategory>(key) + 具名 APIMarkKilled/IsKilled 等)
  • OnStateChanged 事件供 UI 响应式刷新
  • LoadFromSave/GetAllFlags 与存档完整对接

RoomTransition★★★★★

双触发模式Auto / Interact+ 钥匙物品校验:

public bool   CanInteract    => !_autoTrigger;
public string InteractPrompt => "前往下一区域";

OnDrawGizmos 可视化碰撞体,编辑器友好。

MovingPlatform★★★★☆

Kinematic RB2D + WayPoint 系统设计完整三种模式LinearAB/WayPoints/TriggeredLinear复用同一 FixedUpdate 逻辑。发现一处 WaitForSeconds 未缓存(见 §三 TD-10


2.8 UI 层

HUDController★★★★☆

纯事件驱动,所有订阅通过 CompositeDisposable 管理,OnEnable/OnDisable 对称。RebuildHPCells 使用 Instantiate/Destroy见 §三 TD-09,属于低频且可接受的代价。

SaveSlotController / SaveSlotUI★★★★★

async void OnEnableawait RefreshAsync() 的模式正确OnEnable 不能改为 Task但异步方法封装在 Task-returning 方法中)。SelectSlotAsync 处理新游戏/继续的分支清晰。

RebindPanel★★★★★

排他锁设计优雅:

foreach (var row in _rows)
    row.SetInteractable(row == requestingRow);  // 只允许点击的那行可交互

重绑定完成后自动调用 SaveBindingOverrides(),持久化无遗漏。


2.9 Quest 层

QuestManager★★★★☆

  • _questIndex Dictionary<string, QuestSO> O(1) 查找,性能优秀
  • ISaveable 接口完整实现
  • RewardSO.Apply(PlayerStats player) 导致 BaseGames.Quest 程序集依赖 BaseGames.Player,违反依赖方向原则(见 §三 TD-11

2.10 Support 层

PlatformBootstrap★★★★★

  • DefaultExecutionOrder(-200) 早于游戏逻辑
  • async Awake 序列化初始化步骤
  • #if UNITY_STANDALONE && STEAMWORKS_NET 编译期平台分离
  • NullPlatformService 优雅降级

AntiSoftlockSystem★★★★★

  • TransformEventChannelSO 获取玩家引用,替代 FindObjectsOfTypev9 已修复)
  • 速度阈值 + 帧数窗口检测卡死
  • 逃脱路径列表 _escapePaths,多出口设计

SpeedrunTimer★★★★★

速通计时器的优化细节体现了对性能的认真态度:

int currentSecond = (int)ElapsedSeconds;
if (currentSecond != _lastDisplayedSecond)   // 仅整秒数变化时重建字符串
{
    _lastDisplayedSecond = currentSecond;
    UpdateDisplay();
}

Time.unscaledDeltaTime 免受 HitStop timeScale 影响。ISaveable 实现持久化计时。

AnalyticsManager★★★★★

  • #if !UNITY_EDITOR && !DEVELOPMENT_BUILD 生产环境才激活
  • 只收集玩法数据boss_kill/room_enter 等),无 PII
  • 本地 JSON 批量写入,_flushThreshold 控制 IO 频率
  • ServiceLocator 注册IAnalyticsService 接口解耦

AccessibilityManager★★★☆☆

功能完整(色盲模式/屏幕震动/减闪/大文字),但 CanPlayScreenShake() 是静态方法直接访问 _instance,与全框架 ServiceLocator 模式不一致(见 §三 TD-08

CrashReporter★★★★☆

Application.logMessageReceived 捕获崩溃 + OnApplicationPause 紧急存档,完整的崩溃容错链。每条 Error/Exception 单独写一个文件,无频率限制,长期运行可能积累大量崩溃日志文件(见 §三 TD-12


2.11 Editor 工具链(★★★★★)

本框架的编辑器工具链已达到商业发行级水准:

工具 功能 亮点
EventBusMonitorWindow SO 事件总线监控 Filter / Pause / Auto ScrollPlay Mode 实时刷新
AddressKeyValidator Addressable Key 有效性验证 Build Pre-process 钩子(callbackOrder = 0),孤儿 Key 导致构建失败
SOValidationRunner ScriptableObject 字段验证 Build Pre-process 钩子(callbackOrder = 1),序列化完整性检查
AddressReferenceGraphWindow 资产引用关系图 可视化依赖分析

AddressKeyValidatorBuildHook 的 callbackOrder = 0 在 SOValidationRunner(callbackOrder = 1) 之前执行,执行顺序显式管理,防止漏检。


三、发现的问题列表

TD-06 — InputReaderSO: HandlePause FindPauseChannelByName 全量扫描

位置: Assets/Scripts/Input/InputReaderSO.cs
严重程度: 中
描述: _onPauseRequested 为 null 时回退 Resources.FindObjectsOfTypeAll<VoidEventChannelSO>() 按名称扫描所有已加载 SO。这是 O(n) 全资产扫描,且违反"框架不兜底、Inspector 强制赋值"原则。

修复方案: 移除 FindPauseChannelByName() 方法,改为 Debug.Assert 强制要求 Inspector 赋值。


TD-07 — EmergencySaveService: PromoteToSlot 绕过 ISaveStorage 抽象

位置: Assets/Scripts/Core/Save/EmergencySaveService.cs
严重程度: 中
描述: PromoteToSlot 直接 new LocalFileStorage(),绕过 ISaveStorage 接口和 SaveManager 的封装,导致未来若替换为云存储/加密存储时该路径失效。

修复方案: 在 SaveManager 上暴露 PromoteEmergencyToSlot(int targetSlot) 方法,所有 IO 操作通过 _storage 字段进行,EmergencySaveService.PromoteToSlot 委托给 _saveManager


TD-08 — AccessibilityManager: 静态方法绕过 ServiceLocator

位置: Assets/Scripts/Support/Accessibility/AccessibilityManager.cs
严重程度: 低-中
描述: CanPlayScreenShake() 是静态方法,通过 _instance 直接访问,与全框架 ServiceLocator 模式不一致。FeedbackSystem 等调用方被迫依赖具体类型而非 IAccessibilityService 接口。

修复方案: 在 Awake 中注册 ServiceLocator.Register<IAccessibilityService>(this), 将 CanPlayScreenShake() 改为接口方法,调用方改用 ServiceLocator.GetOrDefault<IAccessibilityService>()?.CanPlayScreenShake()


TD-09 — HUDController: RebuildHPCells Instantiate/Destroy

位置: Assets/Scripts/UI/HUD/HUDController.cs
严重程度: 低MaxHP 变化极低频)
描述: _onMaxHPChanged 触发时 Destroy 所有旧 HP Cell 再 Instantiate 新的,无 Object Pooling。对于 HP 上限扩展(>当前数量)可以改为 SetActive 复用。

修复方案: 先 SetActive 复用已有 Cell仅在数量不足时 Instantiate 补充,超出时 SetActive(false) 而非 Destroy。


TD-10 — MovingPlatform: WaitAndAdvance 每次 new WaitForSeconds

位置: Assets/Scripts/World/MovingPlatform.cs
严重程度: 低
描述: WaitAndAdvance 协程每次执行时 yield return new WaitForSeconds(_waitAtEndpoint) 分配新实例,与 BossSkillExecutor 中已实施的 WFS 缓存策略不一致。

修复方案: 增加 private WaitForSeconds _waitForEndpoint 缓存字段,在 Awake 中初始化。


TD-11 — RewardSO: Quest 程序集依赖 Player 程序集

位置: Assets/Scripts/Quest/RewardSO.cs
严重程度: 中
描述: RewardSO.Apply(PlayerStats player) 使 BaseGames.Quest 程序集对 BaseGames.Player 产生直接依赖违反单向依赖原则Quest 层级应独立于 Player 实现)。若未来替换 PlayerStats 或提取到其他程序集,会导致 Quest 编译失败。

修复方案: 定义 IRewardTarget 接口(放在 BaseGames.CoreBaseGames.Quest 中),PlayerStats 实现该接口,RewardSO.Apply(IRewardTarget target) 只依赖接口。


TD-12 — CrashReporter: 每条错误单独写文件无频率限制

位置: Assets/Scripts/Core/Save/CrashReporter.cs
严重程度: 低
描述: OnLogMessage 对每条 Exception/Error 都调用 WriteDiagnosticLog 写入独立文件,长时间运行的游戏在出错频繁时会在 persistentDataPath 中积累大量 crash_*.log 文件,影响存储占用和 IO 性能。

修复方案: 增加频率限制(同一帧/同一秒内最多写 1 条),并设置最大保留文件数(保留最新 N 个,超出时删除最旧文件)。


四、v10 新增亮点汇总

以下是 v1-v9 中未覆盖、本次全量评审新发现的亮点实现:

亮点 位置 说明
SaveData.ExtensionData [JsonExtensionData] Core/Save/SaveData.cs 存档前向兼容,未来字段不丢失
SaveData.DLC Dictionary<string, JObject> Core/Save/SaveData.cs DLC 扩展节点,零侵入
SaveMigrator fall-through 迁移链 Core/Save/SaveMigrator.cs 每次升级追加一个 if 块,旧逻辑不修改
DifficultyManager.SteelSoul 单向锁 Core/Difficulty/DifficultyManager.cs SteelSoul 模式中途无法降级
ClashResolver 有序元组 HashSet 去重 Combat/ClashResolver.cs 每帧双方各触发一次的拼刀去重
BatchLOSSystem 轮询降级策略 Enemies/AI/BatchLOSSystem.cs 2022.3 Job2D 不稳定的正确应对
InputBuffer 具名 handler Input/InputBuffer.cs 保证委托对称取消订阅
SpeedrunTimer 整秒节流显示 Support/Speedrun/SpeedrunTimer.cs 每帧字符串分配归零
FormController 三层通知 Player/FormController.cs SO 事件 + C# 事件 + SkillHUD 刷新分层解耦
SkillManager 固定数组快照 Skills/SkillManager.cs GC-free Update 冷却遍历
RebindPanel 排他锁 UI/Settings/RebindPanel.cs 同时只允许一行处于重绑定状态
AddressKeyValidatorBuildHook Editor/AddressKeyValidator.cs 孤儿 Key 触发构建失败,资产完整性强保证
AnalyticsManager 本地无 PII 分析 Support/Analytics/AnalyticsManager.cs 仅玩法数据 + 批量 IO
SaveSlotController async OnEnable UI/Menus/SaveSlotController.cs async Task 封装正确OnEnable 签名合规

五、修复计划

按优先级排序,共 6 个可修复问题TD-06 至 TD-11TD-12 优先级最低):

优先级 ID 文件 修复类型
TD-11 Quest/RewardSO.cs 引入 IRewardTarget 接口,解除跨程序集依赖
TD-06 Input/InputReaderSO.cs 移除 FindPauseChannelByName 全量扫描回退
TD-07 Core/Save/EmergencySaveService.cs PromoteToSlot 委托给 SaveManager
TD-08 Support/Accessibility/AccessibilityManager.cs 注册为 IAccessibilityService
TD-09 UI/HUD/HUDController.cs HP Cell 改用 SetActive 复用
TD-10 World/MovingPlatform.cs 缓存 WaitForSeconds
TD-12 Core/Save/CrashReporter.cs 崩溃日志频率限制 + 最大文件数

六、总体结论

Zeling v2 框架在约 350 个 C# 源文件的全量审查中,表现出一致、成熟、高度内聚的商业游戏框架水准:

  • 无架构级缺陷程序集依赖图单向核心抽象ServiceLocator / EventChannel / CompositeDisposable全框架统一使用
  • 生产级基础设施存档原子IO + HMAC + 迁移链、崩溃容错、批量LOS、Analytics、无障碍功能齐全
  • 编辑器工具链完整EventBusMonitor、AddressKeyValidator构建钩子、SOValidationRunner 已达到发行标准
  • 扩展性预留充分SaveData DLC 节点、EffectFactory 注册、ISaveable 注册表均已为未来功能铺路
  • 6个可修复问题均为中低优先级,无需停工紧急修复,可在下个迭代统一解决

建议:完成 TD-06 至 TD-11 的修复后,框架可正式进入功能内容制作阶段,无需再做结构性调整。