From 984a7384783db356a7b68f55247e6761d551ce0a Mon Sep 17 00:00:00 2001 From: Joywayer Date: Tue, 12 May 2026 16:45:37 +0800 Subject: [PATCH] =?UTF-8?q?v12=20=E5=85=A8=E9=87=8F=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=EF=BC=9A=E4=BF=AE=E5=A4=8D=20TD-18=EF=BC=88RunState=20?= =?UTF-8?q?=E7=89=A9=E7=90=86=E5=8F=8C=E9=87=8D=E6=96=BD=E9=80=9F=EF=BC=89?= =?UTF-8?q?+=20=E7=BC=96=E5=86=99=20v12=20=E8=AF=84=E5=AE=A1=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scripts/Player/States/RunState.cs | 2 - Docs/Review/FrameworkReview_2026_May_v12.md | 315 ++++++++++++++++++++ 2 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 Docs/Review/FrameworkReview_2026_May_v12.md diff --git a/Assets/Scripts/Player/States/RunState.cs b/Assets/Scripts/Player/States/RunState.cs index 2e43f89..87e480c 100644 --- a/Assets/Scripts/Player/States/RunState.cs +++ b/Assets/Scripts/Player/States/RunState.cs @@ -30,8 +30,6 @@ namespace BaseGames.Player.States _owner.TransitionTo(_owner.GetState()); return; } - - Move.Move(Input.MoveInput.x * Cfg.RunSpeed); } public override void OnStateFixedUpdate() diff --git a/Docs/Review/FrameworkReview_2026_May_v12.md b/Docs/Review/FrameworkReview_2026_May_v12.md new file mode 100644 index 0000000..83f87a8 --- /dev/null +++ b/Docs/Review/FrameworkReview_2026_May_v12.md @@ -0,0 +1,315 @@ +# Zeling v2 框架全量代码评审报告 v12 + +> **评审时间**:2026 年 5 月 +> **评审范围**:v12 深度补充评审(Camera / Localization / Spells / Skills / Player States 全量 / Enemies 核心 / World 核心 / Dialogue / Progression 成就系统) +> **前置版本**:v11 评审报告(综合评分 9.30/10) +> **本版改进**:精读 v11 未覆盖的剩余主要模块;新增亮点 14 条;发现并修复 1 个问题(TD-18) + +--- + +## 一、综合评分总览 + +| 维度 | v11 评分 | v12 评分 | 变化 | 说明 | +|------|---------|---------|------|------| +| 架构设计 | 9.3 | 9.4 | ↑ | NarrativeNPC 条件版本系统 / WorldStateRegistry 泛化分类 / InteractableNPC 继承钩子等设计亮点提升 | +| 性能 | 9.2 | 9.3 | ↑ | SkillManager 固定数组快照避免 GC / EnemyQuotaManager O(1) 注销 + 双结构体加速 / LocalizationManager 双层缓存 | +| 可扩展性 | 9.4 | 9.4 | → | AchievementCondition 开放多态 / DialogueVersion 无代码条件扩展继续保持高分 | +| 编辑器友好 | 9.4 | 9.4 | → | 稳定 | +| 使用便利性 | 9.0 | 9.1 | ↑ | InteractableNPC 三钩子 API 易扩展 / CollectibleSpawner 静态入口简洁 | +| **综合** | **9.30** | **9.35** | **↑** | 本轮精读发现整体已达成熟商业品质,TD-18 物理双重施速修复后无已知隐患 | + +--- + +## 二、v11 修复验证 + +| ID | 修复项 | 验证结果 | +|----|--------|---------| +| TD-13 | IQuestManager.CompleteQuest 改为 IRewardTarget | ✅ 已验证:接口方法签名正确,移除 `using BaseGames.Player` | +| TD-14 | HurtFlashController 缓存 WaitForSeconds | ✅ 已验证:`_waitForFlash` 字段在构造时创建 | +| TD-15 | LiquidType 枚举迁移至 Core.Events | ✅ 已验证:`LiquidType.cs` 存在于 Core.Events 程序集目录,正确枚举类型 | +| TD-16 | LiquidEvent / LiquidZone / WaterDangerState 等改为枚举比较 | ✅ 已验证:字段类型为 `LiquidType`,比较无 `.ToString()` | +| TD-17 | DeathScreenController 订阅时序修复 | ✅ 已验证:`OnEnable` 直接 `StartCoroutine`,不再依赖已失效订阅 | + +--- + +## 三、本轮评审模块详解 + +### 3.1 Camera 模块 + +**文件**:`CameraStateController.cs`、`RoomCamera.cs` + +| 亮点 | 说明 | +|------|------| +| **BlendProfile per-room** | `BlendProfileSO` 以 SO 形式挂在每个房间,支持独立混合曲线和时长,房间过渡效果可精细定制 | +| **HashSet 注册管理** | `_registeredCameras: HashSet` 防止重复注册,`SwitchRoom` O(1) 查找 | +| **Priority 驱动切换** | `RoomCamera.Activate()` 设 priority=15,`Deactivate()` 设 priority=0,Cinemachine Brain 自动选最高优先级,切换逻辑零侵入 | +| **TriggerImpulse 重载** | 提供 `Vector3` 和 `float` 两个重载,震屏 API 简洁易用 | + +**评分**:架构 9.5 / 性能 9.5 / 可扩展性 9.5 + +--- + +### 3.2 Localization 模块 + +**文件**:`LocalizationManager.cs` + +| 亮点 | 说明 | +|------|------| +| **双层缓存** | `Dictionary>`,key = `"ChineseSimplified/UI"`,避免重复解析 JSON | +| **三级回退链** | 当前语言 → English → 直接返回 key,任何情况下不抛异常,UI 不崩溃 | +| **双事件模式** | `static event Action OnLanguageChanged`(向后兼容旧代码)+ 接口 `ILocalizationService.OnLanguageChanged` 显式实现委托给静态事件,两种订阅方式都得到支持 | +| **ISaveable 持久化** | 语言偏好通过 SaveManager 持久化,不使用 PlayerPrefs,与存档系统保持一致 | +| **Resources 加载隔离** | JSON 文件放在 `Resources/Localization/{Language}/{TableName}.json`,文件组织清晰,未来迁移至 Addressables 仅需改一处加载逻辑 | + +**评分**:架构 9.5 / 性能 9.3 / 可扩展性 9.4 + +--- + +### 3.3 Spells 模块 + +**文件**:`SpellManager.cs`、`SpellSO.cs` + +| 亮点 | 说明 | +|------|------| +| **SpellEffectType 完整枚举** | `{Projectile, AreaOfEffect, SelfBuff, SummonShade, TeleportBlink}` 覆盖主流法术类型,扩展时直接加枚举成员 | +| **CooldownFraction 属性** | `public float CooldownFraction` 供 UI 直接轮询进度,无需 UI 层了解冷却实现细节 | +| **单槽设计简洁** | 当前为单法术槽,`EquipSpell/UnequipSpell` API 清晰,未来扩展多槽只需更换集合结构 | +| **资源类型分离** | `SpellResourceType {SoulPower, SpiritPower}` 与玩家两种资源对应,条件检查统一在 `TryCastSpell()` | + +**评分**:架构 9.2 / 性能 9.3 / 可扩展性 9.3 + +--- + +### 3.4 Skills 模块 + +**文件**:`SkillManager.cs` + +| 亮点 | 说明 | +|------|------| +| **固定大小数组快照** | `FormSkillSO[] _activeSkills = new FormSkillSO[3]`,`UpdateSkillSet` 写入固定位置,避免每帧遍历 `Dictionary.Values` 产生 GC | +| **Form 注入解耦** | `UpdateSkillSet(soul, spirit1, spirit2)` 由 `FormController` 在 Form 切换时调用,SkillManager 不感知 Form 实现 | +| **Dictionary 按引用键** | `Dictionary _cooldowns` 以 SO 引用为键,精确对应每个技能实例的冷却 | +| **三独立 Input 事件** | SoulSkillEvent / SpiritSkill1 / SpiritSkill2 各自订阅,互不干扰 | + +**评分**:架构 9.3 / 性能 9.5 / 可扩展性 9.2 + +--- + +### 3.5 Player States 全量评审 + +#### 已评(v11 本批) + +| 状态 | 亮点 | 评分 | +|------|------|------| +| **IdleState** | Enter 时 `AerialDashState.ResetAerialDashes()` 着地重置,设计精准 | 9.5 | +| **RunState** | ~~双重 Move 调用~~ → **TD-18 已修复** | 9.3 | +| **JumpState** | `Input.JumpCancelledEvent` 精确 Enter/Exit 管理,`CutJump()` 可变跳跃高度 | 9.5 | +| **FallState** | 郊狼时间 + `FallGravityMult` 增强下落手感 + `MaxFallSpeed` 上限,三重机制健全 | 9.5 | +| **DashState** | `SetGravityScale(0f)` 冲刺间禁重力 + FixedUpdate 锁速防摩擦 + `IsInvincible` 无敌帧 | 9.5 | +| **WallSlideState** | Enter/Exit 精确订阅 `JumpStartedEvent`,离墙/着地转出逻辑清晰 | 9.4 | +| **WallJumpState** | `_inputLockTimer` 锁定水平输入防止立即贴回墙壁,手感设计合理 | 9.4 | +| **AerialDashState** | 独立于地面冲刺,消耗次数计数,任意方向冲刺 | 9.4 | +| **AttackState** | Animancer 归一化时间事件驱动 HitBox,连击段数由 `AnimCfg.GroundAttacks.Length` 动态决定,零硬编码 | 9.5 | +| **ParryState** | `OpenParryWindow/CloseParryWindow` 生命周期严格对齐 Enter/Exit,OnEnd 动画事件驱动退出 | 9.5 | +| **DeadState** | 冻结物理 + 关重力 + 禁 HurtBox,Exit 时全部恢复,"不自动转出"由 DeathRespawnSystem 事件驱动 | 9.5 | + +**总体 Player States 评分**:9.45 / 10 + +--- + +### 3.6 Enemies 模块 + +**文件**:`EnemyBase.cs`、`EnemyNavAgent.cs`、`EnemyQuotaManager.cs`、`LootTableSO.cs`、`LootResolver.cs` + +| 亮点 | 说明 | +|------|------| +| **IPathAgent 接口隔离** | `EnemyBase._nav: IPathAgent`,`EnemyNavAgent` 实现接口,EnemyBase 和 BD Task 不直接依赖 PathBerserker2d 类型 | +| **ILOSRequester 接口** | `EnemyBase: ILOSRequester`,视线检测系统通过接口查询,架构层次清晰 | +| **EnemyQuotaManager 双结构体** | `HashSet` O(1) 重复检测 + `List` 排序 + `Dictionary` 索引映射,注销时 swap-remove 保持 O(1) | +| **每 10 帧重排** | `REBALANCE_INTERVAL = 10`,只对最近 N 个敌人启用 BT,大幅节省 AI 计算开销 | +| **OnPlayerSpawned 替代 FindWithTag** | 通过 `TransformEventChannelSO` 接收玩家 Transform,避免 N 个敌人同帧全场景 Tag 扫描 | +| **LootResolver 加权随机** | 两次遍历 `LootTable.Entries`(一次求总权重,一次滚动选中),Hard 难度对 `ScaleWithDifficulty` 条目加 1.5× 权重 | +| **IPoiseSource 霸体分离** | `EnemyBase` 持有 `IPoiseSource _poiseSource`,由 `EnemyPoiseComponent.Awake()` 自动注入,TakeDamage 时读取霸体等级 | + +**评分**:架构 9.4 / 性能 9.4 / 可扩展性 9.3 + +--- + +### 3.7 World 模块 + +**文件**:`WorldStateRegistry.cs`、`RoomController.cs`、`HazardZone.cs`、`CrumblePlatform.cs`、`PhantomPlate.cs`、`FalseWall.cs`、`DirectionalDestructible.cs`、`WorldMarker.cs`、`CollectibleSpawner.cs` + +| 亮点 | 说明 | +|------|------| +| **WorldStateRegistry 泛化分类** | `WorldObjectCategory` 枚举 + 语义化快捷 API(`IsCollected / MarkSavePointActivated / IsDoorOpened`),单一 SO 管理全部世界状态 | +| **OnEnable 清状态** | SO 的 `OnEnable` 在每次 Play Mode 重置 `_states`,Editor 迭代不污染 | +| **OnStateChanged 事件** | `(WorldObjectCategory, string)` 响应式广播,UI / 成就系统零耦合订阅 | +| **RoomController 最简职责** | 仅负责切换相机 + 出生点查询,无多余逻辑,符合 SRP | +| **PhantomPlate 原生 PlatformEffector2D** | 利用 Unity 内置单向平台,`TriggerDropThrough()` 接口简洁,无需物理 Hack | +| **CrumblePlatform 四态协程** | Warning → Crumbling → Gone → Recovering,`_isOneShot` 控制是否恢复,Feedback 统一由 MMF_Player 触发 | +| **DirectionalDestructible 模式匹配** | `switch expression` 将攻击方向映射到法向量阈值,继承 `DestructibleTile` 仅覆盖 `CheckDestroyCondition`,遵循 OCP | +| **WorldMarker Gizmo 颜色** | 按 `WorldMarkerType switch` 分色 Gizmo,编辑器可视化区分标记类型 | +| **CollectibleSpawner 池优先 + 回退** | 优先 `IObjectPoolService.Spawn`,不可用时 `Object.Instantiate` 兜底,编辑器 / 测试场景无需预热池 | +| **HazardZone stats.MaxHP*2 即死** | 用 `MaxHP*2` 而非 `int.MaxValue` 确保即死,同时避免整数溢出 | + +**评分**:架构 9.4 / 性能 9.3 / 可扩展性 9.4 + +--- + +### 3.8 Dialogue 模块 + +**文件**:`InteractableNPC.cs`、`NarrativeNPC.cs`、`DialogueUI.cs` + +| 亮点 | 说明 | +|------|------| +| **InteractableNPC 三钩子** | `Interact_Internal()`(前置逻辑)、`GetCurrentDialogue()`(对话选择)、`PlayDialogue()`(播放),子类覆盖任意钩子即可扩展行为 | +| **NarrativeNPC 优先级排列** | `DialogueVersion[]` 按优先级遍历,`CheckConditions` AND requiredFlags / NOT blockedByFlags,无代码扩展对话版本 | +| **DialogueUI 打字机** | `SkipTyping()` 立即显示全文,`IsTyping` 属性外部可查询,`_continuePrompt` 对象打字结束后显示,交互反馈完整 | +| **ServiceLocator 解耦** | `InteractableNPC.PlayDialogue` 通过 `ServiceLocator.GetOrDefault()` 获取管理器,不直接引用具体实现 | + +**评分**:架构 9.4 / 性能 9.3 / 可扩展性 9.5 + +--- + +### 3.9 Progression / Achievement 模块 + +**文件**:`BossProgressTracker.cs`、`HPContainerPickup.cs`、`ProgressLock.cs`、`ParryCountCondition.cs`、`NoHealRunCondition.cs`、`TimedBossKillCondition.cs`、`DefeatedAllBossesCondition.cs` + +| 亮点 | 说明 | +|------|------| +| **AchievementCondition 多态 SO** | 每个条件类型独立 SO 子类,`CreateAssetMenu` 直接在 Editor 创建,条件组合无代码修改 | +| **ParryCountCondition GetProgress** | 实现 `GetProgress()` 返回 `[0,1]` 进度,UI 可显示进度条,成就系统 API 完整 | +| **NoHealRunCondition Switches 标志** | 通过 `SaveData.World.Switches` 记录治疗失败状态,而非运行时字段,持久化后可跨场景检查 | +| **TimedBossKillCondition ChallengeRooms** | 读取 `ChallengeRooms.Records[bossRoomId].BestTime`,与挑战房间计时系统直接集成 | +| **BossProgressTracker 事件路由** | `_onBossDefeated` 接收来自 `BossCombat` 的事件,过滤 `_bossId` 后通过 `_onBossDefeatedForSave` 转发给 SaveSystem,零耦合 | +| **ProgressLock Start + OnEnable 双触发** | `Start` 读档状态初始化,`OnEnable` 订阅事件,事件驱动实时响应 Boss 击败,状态机设计严谨 | +| **HPContainerPickup ISaveService 查询** | `Start()` 通过 `ISaveService.IsWorldCollected` 检查存档防重复拾取,`OnTriggerEnter2D` 再次校验防竞态 | + +**评分**:架构 9.3 / 性能 9.3 / 可扩展性 9.5 + +--- + +## 四、发现问题与修复 + +### TD-18:RunState 双重施加水平速度 + +| 属性 | 内容 | +|------|------| +| **ID** | TD-18 | +| **严重程度** | 中 | +| **文件** | `Assets/Scripts/Player/States/RunState.cs` | +| **问题描述** | `OnStateUpdate()` 和 `OnStateFixedUpdate()` 均调用 `Move.Move(Input.MoveInput.x * Cfg.RunSpeed)`。`PlayerController.Update()` 调用 `OnStateUpdate()`,`PlayerController.FixedUpdate()` 调用 `OnStateFixedUpdate()`。在一个渲染帧内可能执行多次 `FixedUpdate`(Physics 步骤 > 渲染帧时),或水平速度已由 `Update` 设置后再由 `FixedUpdate` 覆盖,导致物理帧内速度被双重施加或互相干扰。 | +| **根因** | 物理移动逻辑(`Move.Move`)应统一放在 `FixedUpdate` 中;`Update` 中仅做输入轮询和状态转换判断。 | +| **修复方案** | 移除 `OnStateUpdate()` 中的 `Move.Move(...)` 调用,仅保留状态转换检查;物理移动仅在 `OnStateFixedUpdate()` 中执行。 | +| **修复状态** | ✅ **已修复** | + +**修复前**: +```csharp +public override void OnStateUpdate() +{ + // ...转换检查... + if (Mathf.Abs(Input.MoveInput.x) < 0.1f) { ... return; } + Move.Move(Input.MoveInput.x * Cfg.RunSpeed); // ← 多余 +} +public override void OnStateFixedUpdate() +{ + Move.Move(Input.MoveInput.x * Cfg.RunSpeed); +} +``` + +**修复后**: +```csharp +public override void OnStateUpdate() +{ + // 仅做状态转换检查,不施加速度 + if (!Move.IsGrounded) { ... return; } + if (Buffer.ConsumeJump()) { ... return; } + if (Mathf.Abs(Input.MoveInput.x) < 0.1f) { ... return; } +} +public override void OnStateFixedUpdate() +{ + Move.Move(Input.MoveInput.x * Cfg.RunSpeed); // 物理移动统一在此 +} +``` + +--- + +## 五、本版代码亮点汇总(新增 14 条) + +| 编号 | 模块 | 亮点 | +|------|------|------| +| #31 | Camera | `BlendProfileSO` per-room,每个房间独立相机混合配置 | +| #32 | Camera | `RoomCamera` Priority 驱动切换,Cinemachine Brain 零侵入 | +| #33 | Localization | 三级回退链(当前语言→English→key),任何情况不抛异常 | +| #34 | Localization | 双事件模式(静态 `OnLanguageChanged` + 接口),两种订阅方式共存 | +| #35 | Localization | `ISaveable` 持久化语言偏好,不使用 PlayerPrefs | +| #36 | Skills | `FormSkillSO[] _activeSkills` 固定数组快照,避免 Update 遍历 Dictionary GC | +| #37 | Player States | Animancer 归一化时间事件驱动 HitBox,连击段数动态读取,AttackState 零硬编码 | +| #38 | Player States | `AerialDashState` 独立次数计数,支持多段空中冲刺扩展 | +| #39 | Enemies | `EnemyQuotaManager` swap-remove O(1) 注销 + 每 10 帧重排 BT 配额 | +| #40 | Enemies | `LootResolver` 加权随机 + 难度缩放,`CollectibleSpawner` 池优先 + 回退 | +| #41 | World | `WorldStateRegistry` 泛化分类 API + `OnStateChanged` 响应式广播 | +| #42 | World | `DirectionalDestructible` 模式匹配方向检测,继承 OCP 扩展 | +| #43 | Dialogue | `NarrativeNPC` 优先级版本列表 + AND/NOT 标志条件,零代码扩展对话 | +| #44 | Progression | `AchievementCondition` 多态 SO + `GetProgress()` 进度 API 完整 | + +--- + +## 六、历史亮点回顾(v1–v11 累计 30 条) + +> 见 [FrameworkReview_2026_May_v11.md](FrameworkReview_2026_May_v11.md) 第五节。 + +--- + +## 七、历史问题修复汇总(TD-01 至 TD-18) + +| ID | 严重程度 | 版本 | 状态 | +|----|---------|------|------| +| TD-01 | 高 | v5 | ✅ | +| TD-02 | 中 | v5 | ✅ | +| TD-03 | 高 | v6 | ✅ | +| TD-04 | 中 | v7 | ✅ | +| TD-05 | 低 | v7 | ✅ | +| TD-06 | 中 | v10 | ✅ | +| TD-07 | 高 | v10 | ✅ | +| TD-08 | 中 | v10 | ✅ | +| TD-09 | 低 | v10 | ✅ | +| TD-10 | 中 | v10 | ✅ | +| TD-11 | 低 | v10 | ✅ | +| TD-12 | 低 | v10 | ✅ | +| TD-13 | 高 | v11 | ✅ | +| TD-14 | 低 | v11 | ✅ | +| TD-15 | 低 | v11 | ✅(LiquidType 枚举迁移) | +| TD-16 | 低 | v11 | ✅(字符串比较→枚举比较) | +| TD-17 | 中 | v11 | ✅ | +| TD-18 | 中 | v12 | ✅ | + +--- + +## 八、遗留待覆盖模块 + +以下模块尚未精读(规模较小,估计代码质量与已读部分一致): + +- `Camera/CameraBlendProfileSO.cs`、`CameraTriggerZone.cs`、`ICameraService.cs`、`RoomVisibleArea.cs` +- `Skills/FormSkillSO.cs`、`SkillModifierRegistry.cs`、`SkillSlotNames.cs` +- `VFX/VFXCatalogSO.cs` +- `Enemies/AI/`(BT Task 集合)、`Enemies/Boss/`(Boss Patterns) +- `World/BreadcrumbTracker.cs`、`MagicWall.cs`、`DeathShade.cs`、`RoomTransition.cs`、`SavePoint.cs`、`AbilityGate.cs`、`AbilityUnlock.cs` +- `Progression/RegionDefinitionSO.cs` +- `Progression/Achievement/`(其余 5 个条件类) + +--- + +## 九、总结 + +v12 评审精读了 Camera / Localization / Spells / Skills / Player States 全量 / Enemies 核心 / World 核心 / Dialogue / Progression 成就系统共约 40+ 文件,发现并修复 1 个中等问题(TD-18 RunState 物理双重施速)。 + +框架整体达到成熟商业 2D Action RPG 代码品质: + +- **架构**:模块边界清晰,ServiceLocator + EventChannel 解耦充分,单向程序集依赖无循环 +- **性能**:关键热路径(玩家状态 Update / 敌人 AI 配额 / 技能快照)均已优化,无明显 GC 热点 +- **可扩展性**:SO 多态(AchievementCondition / CharmEffect / SpellSO / LootTableSO)覆盖核心变化点,设计师友好 +- **编辑器友好**:Gizmo 可视化(HazardZone / WorldMarker / DirectionalDestructible)+ `[RequireComponent]` + `Debug.Assert` 覆盖完整 +- **使用便利性**:InteractableNPC 三钩子 / WorldStateRegistry 语义化 API / CollectibleSpawner 静态入口,扩展体验优秀 + +综合评分:**9.35 / 10**(无已知未修复问题)