Files
zeling_v2/Docs/Review/CodeReview_2025_Full.md
2026-05-12 15:34:08 +08:00

547 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 泽灵 v2 — 全面代码评估报告
> 评估基准Unity 2022.3 LTS / C# / 2D 动作平台游戏
> 评估标准:商业高性能游戏代码实践(参考 Hollow Knight、Celeste、Hades 等同类产品)
> 评估范围:`Assets/Scripts` 全量代码(约 330 个 .cs 文件)
> 评估日期2025/2026
---
## 评分总览
| 评估维度 | 得分 | 备注 |
|--------------|------|-------------------------------|
| 架构设计 | 8.5 | 模块化扎实,少量遗留单例 |
| 性能 | 8.0 | 热路径零分配,批量 LOS 优秀 |
| 可扩展性 | 8.5 | 数据驱动完善,少量硬编码管道 |
| 编辑器友好 | 9.0 | 工具链完整,为同类 Indie 最佳水平 |
| 使用便利性 | 8.0 | 契约清晰,命名偶有不一致 |
| **综合** | **8.4** | **接近商业发行水准** |
---
## 一、架构设计8.5 / 10
### 1.1 程序集隔离(✅ 优秀)
项目共 **25+ 个程序集定义(.asmdef**,粒度精确到子模块层级:
```
BaseGames.Core → 全局服务基础设施
BaseGames.Core.Save → 持久化(单向依赖 Core
BaseGames.Core.Events → SO 事件频道(无外部依赖)
BaseGames.Combat → 战斗管道(无 Player/Parry 直接依赖)
BaseGames.Parry → 弹反(依赖 Combat 接口,不依赖 Player
BaseGames.Player → 玩家逻辑(处于依赖图顶层)
BaseGames.Enemies.AI → BD 任务节点
BaseGames.Editor → 仅编辑器工具
...
```
编译时强制模块边界,跨模块引用必须使用接口或事件频道,与商业游戏标准一致。
**依赖方向控制**
```
Core → Core.Save单向
GameServiceRegistrar桥接使用 SaveServiceAdapter 适配器,
避免 Core.Save → Core 的反向引用,保持有向无环图。
```
### 1.2 服务定位器(✅ 优秀)
`ServiceLocator``BaseGames.Core`
```csharp
ServiceLocator.Register<IAudioService>(this); // 覆盖注册
ServiceLocator.RegisterIfAbsent<IAudioService>(null); // 防重复
ServiceLocator.GetOrDefault<ICameraService>() // 安全取用
```
- 接口隔离:调用方依赖 `IAudioService`,不依赖 `AudioManager` 具体类
- Null-Object 兜底:`GameServiceRegistrar.Awake()` 先注册 `NullAudioService`,避免 `AudioManager` 未初始化时的空引用崩溃
- 测试支持:`OverrideForTest<T>` / `Reset()` 仅在 `#if UNITY_EDITOR` 暴露
**已注册接口**
`IAudioService` / `ISaveService` / `ISceneService` / `IDeathRespawnService` / `IEventChannelRegistry` / `IObjectPoolService` / `ICameraService` / `IPlatformService`
### 1.3 游戏状态机(✅ 优秀)
`GameStateMachine` 为纯 C# 类,**不继承 MonoBehaviour**
```csharp
// 非法转换 → 返回 false + 错误字符串(不抛出异常)
bool ok = _fsm.TransitionTo(nextId, out string error);
```
- 每个状态声明 `ValidNextStates`(合法出口白名单),状态图文档化在代码中
- `GameManager` 持有 `GameStateMachine` 实例而非继承它(组合优于继承)
- 与 Celeste 等游戏的 `StateMachine` 模式完全吻合
### 1.4 玩家状态机(✅ 优秀)
`PlayerController` 维护 `Dictionary<Type, PlayerStateBase>` 状态字典;
`PlayerStateBase` **POCO 类**,不继承 MonoBehaviour生命周期由 `PlayerController` 驱动:
```
OnStateEnter → OnStateUpdate → OnStateFixedUpdate → OnStateExit
```
12 个具体状态Idle / Run / Jump / Fall / Dash / AerialDash / Attack / AirAttack / UpAttack / DownAttack / Parry / Hurt / Dead / Spring / WallSlide / WallJump / Swim各自独立新增状态只需实现 `PlayerStateBase` 并在 `InitializeStates()` 中注册。
**#if UNITY_EDITOR ValidTransitions** 仅在编辑期验证,不增加运行时开销。
### 1.5 敌人状态系统(✅ 优秀 + 尚有空间)
Phase 1 双轨实现:`EnemyStateType` 枚举保持对外 API`IEnemyState` POCO 对象承载逻辑:
```csharp
_stateObjs[EnemyStateType.Hurt] = new EnemyHurtState();
// 子类可在 base.Awake() 后替换:
_stateObjs[EnemyStateType.Hurt] = new BossSpecialHurtState();
```
`ForceState()` 完成三步 Exit → 赋值 → Enter干净无副作用。
**缺陷**`EnemyBase.TakeDamage()` 中 TODO
```csharp
// TODO: 根据霸体结果选 Stagger / Hurt
ForceState(EnemyStateType.Hurt); // 霸体判断尚未走 POCO 路径
```
实际 Stagger 触发仍绕过 `_stateObjs` 字典——Phase 1 双轨不完整。
### 1.6 SO 事件频道(✅ 优秀)
`BaseEventChannelSO<T>` / `VoidBaseEventChannelSO` 双泛型基类:
- `Subscribe()` 返回 `EventSubscription``IDisposable`),配合 `CompositeDisposable.AddTo()` 零泄漏
- Editor 模式下 `EventBusMonitor.Record()` 记录所有事件(帧号 + 订阅数),供 `EventBusMonitorWindow` 运行时调试
- `description` 字段:设计师在 Inspector 中注释频道用途
### 1.7 遗留问题(⚠️)
| 问题 | 文件 | 影响 |
|------|------|------|
| `GameManager.Instance` 静态单例与 `ServiceLocator` 并存 | `GameManager.cs` | 双入口访问模式 |
| `AudioManager.Instance` 标注 `[Obsolete]` 但仍存在 | `AudioManager.cs` | 新代码可能误用旧入口 |
| `VFXPool.Instance` 未注册到 ServiceLocator | `VFXPool.cs` | 无法 Mock / 测试 |
| `GlobalObjectPool.Instance` 保留(已注册 IObjectPoolService但静态 Instance 共存) | `GlobalObjectPool.cs` | 同上 |
| `SaveManager.Instance` 保留(由 DeathRespawnService 直接调用) | `DeathRespawnService.cs` | 依赖具体类 |
| `AntiSoftlockSystem` 在全局命名空间(无 namespace | `AntiSoftlockSystem.cs` | 命名空间污染 |
| `EquipmentManager` 使用 `EventChannelRegistry.Instance`(非 ServiceLocator | `EquipmentManager.cs` | 不一致 |
---
## 二、性能8.0 / 10
### 2.1 战斗热路径——零堆分配(✅ 优秀)
`DamageInfo`**struct**(非 class通过两种方式构造
```csharp
// 热路径:零堆分配,直接从 SO 初始化
DamageInfo info = DamageInfo.From(damageSourceSO);
info.KnockbackDirection = ...;
// 复杂情况Builder 模式(分配一个 Builder 对象,可接受)
DamageInfo info = new DamageInfo.Builder()
.SetRaw(50).SetType(DamageType.Slash).SetFlags(DamageFlags.CanBeParried)
.Build();
```
`HurtBox.ReceiveDamage()` 8步流水线中无任何 LINQ / Alloc 调用,性能关键路径完全符合商业标准。
### 2.2 批量视线检测(✅ 优秀)
`BatchLOSSystem` 实现时间切片策略:
```
每 FixedUpdate 仅处理 min(_maxRequestersPerFrame=8, total) 个请求者
_currentOffset 轮询偏移,均匀分配负载
```
对比朴素实现(每敌人每帧一次 Raycast2D20 个敌人 → 减少 60%+ 射线调用。
**注**`_results[idx]` 写入后未被读取(结果已通过 `requester.ReceiveLOSResult()` 直接回调),`_results` List 是冗余字段,可删除。
### 2.3 对象池(✅ 优秀)
`GlobalObjectPool` 特性:
| 特性 | 实现 |
|------|------|
| Addressables 预热 | `WarmupAsync()` 异步批量实例化 |
| LRU 回收 | MaxCount 达限时回收 LinkedList 头节点O(1) |
| 后台补池 | 同步 Instantiate 后触发协程异步补充 |
| 接口隔离 | 实现 `IObjectPoolService`,可 Mock 测试 |
`VFXPool`ParticleSystem 专用池独立维护合理的关注点分离ParticleSystem 生命周期与普通 GameObject 不同)。
**可改进**`VFXPool.Play()` 每次都通过协程启动,即使对象已在池中可同步取出,协程调度有 1-2 帧延迟。
### 2.4 音频池(✅ 良好)
`AudioManager` 使用 6 源轮转 SFX 池,双 AudioSource 交叉淡入淡出 BGM避免频繁 `AudioSource.Stop/Play` 切换产生的爆音与内存分配。
### 2.5 状态效果双结构(✅ 优秀)
```csharp
private readonly List<StatusEffect> _activeList = new(); // Update 遍历 O(n)
private readonly Dictionary<StatusEffectType, StatusEffect> _activeIndex = new(); // 类型查找 O(1)
```
`MaterialPropertyBlock` 修改 Shader 属性(不修改共享材质),符合 Unity 性能最佳实践。
### 2.6 序列化性能(✅ 良好)
`SaveManager.SaveAsync()` 使用 `Formatting.None`(减少 JSON 体积),`SemaphoreSlim(1,1)` 防止并发写入损坏。
### 2.7 性能风险点(⚠️)
| 风险 | 位置 | 说明 |
|------|------|------|
| `FindObjectsOfType<AudioListener>` | `GameServiceRegistrar.EnsureSingleAudioListener()` | 已有 `_primaryListener` 绑定后可绕过,但未绑定时仍全场景扫描 |
| `_equipped.Sum(c => c.notchCost)` LINQ | `EquipmentManager.UsedNotches` | 每次 UI 查询触发 LINQ列表小<8时可接受建议缓存 |
| `BatchLOSSystem._results` 冗余写入 | `BatchLOSSystem.cs` L68 | 每帧写入后不读取,微小 GC 风险 |
| `VFXPool.PlayCoroutine()` | `VFXPool.cs` | 即使池命中,仍需协程恢复一帧 |
| `PlayerController._states[typeof(T)]` | `PlayerController.cs` | Type 键查找无 boxing引用比较但每次 TransitionTo 需哈希查找 |
---
## 三、可扩展性8.5 / 10
### 3.1 护符系统(✅ 优秀)
`ICharmEffect` 策略模式,完全数据驱动:
```
CharmSO → ICharmEffect[]
├── StatModifierEffect (+攻击力/防御)
├── AttackSpeedEffect (攻速修改)
├── OnHitEffect (命中触发)
├── SkillNumericModifierEffect (技能数值)
├── SkillSlotOverrideEffect (技能槽替换)
├── WeaponOverrideEffect (武器替换)
└── SoulSpellEffect (灵力法术)
```
新增效果只需实现 `ICharmEffect`,无需修改 `EquipmentManager`——完美的开闭原则。
### 3.2 成就系统(✅ 优秀)
`AchievementCondition` 抽象类10 个具体实现:
```
CollectedItemCondition / DefeatedBossCondition / EnteredRegionCondition /
ParryCountCondition / TimedBossKillCondition / MapExplorationCondition ...
```
设计师可在 `AchievementSO` Inspector 中组合条件,不需要代码介入。
### 3.3 Boss 技能系统(✅ 良好)
```
BossBase → EnterPhase(int) [virtual]
→ BossSkillExecutor → BossSkillSO[]
→ SkillSequenceSO (有序技能序列)
→ AttackPatternSO (技能模式 SO)
```
`TelegraphSystem` 独立为组件,可复用于不同 Boss。
### 3.4 EventChain 系统(✅ 良好)
`EventChainSO` 顺序事件链,配合 `EventChainManager` 执行:设计师可在 SO 中定义剧情触发序列,无需写代码。
### 3.5 IValidatable + SOValidationRunner✅ 优秀)
任何 SO 实现 `IValidatable`,自动纳入构建前扫描:
```csharp
public IEnumerable<string> Validate()
{
if (BaseDamage <= 0) yield return "❌ BaseDamage 必须 > 0";
}
```
SOValidationRunner 作为 `IPreprocessBuildWithReport`**构建中止**防止错误配置上线。
### 3.6 可扩展性缺陷(⚠️)
| 问题 | 说明 | 建议 |
|------|------|------|
| `HurtBox.ReceiveDamage()` 8步流水线硬编码 | 新增拦截步骤须修改 `HurtBox` | 引入 `IDamageInterceptor[]` 责任链 |
| `StatusEffectManager.CreateEffect()` | DamageType→StatusEffect 极可能是 switch | 改为 SO 配置映射 `Dictionary<DamageType, StatusEffect>` |
| 无通用属性计算器 | 护符/Buff 效果各自修改 `PlayerStats` 字段 | 考虑 `StatCalculator` 优先级栈(参考 Hades 设计) |
| `AudioManager.PlayBGM/SFX(string)` 为桩 | Phase 2 未完成 | 优先实现 AudioEventSO 集成 |
| `Spells` 模块仅有 `_Placeholder.cs` | 施法系统留空 | 按现有 Skill 模式扩展 |
---
## 四、编辑器友好9.0 / 10
编辑器工具链是本项目**最突出的优势**,接近 AA 商业游戏水准。
### 4.1 工具总览
| 工具 | 位置 | 功能 |
|------|------|------|
| `SOValidationRunner` | `Editor/Validation/` | 全量 SO 数据验证,构建前自动执行 |
| `AddressKeyValidator` | `Editor/` | Addressable Key 有效性验证,防止引用失效 |
| `AddressReferenceGraphWindow` | `Editor/` | Addressable 引用图可视化 |
| `HurtBoxEditor` | `Editor/Combat/` | PlayMode 受击盒注入状态可视化(绿色/橙色) |
| `EventBusMonitorWindow` | `Editor/` | 运行时事件总线监控(频道名 + 订阅数 + 帧号) |
| `EventChannelEditor` | `Editor/` | 事件频道 Inspector 中一键 Raise 按钮 |
| `BossSkillSequenceWindow` | `Editor/` | Boss 技能序列可视化设计器 |
| `EventChainEditorWindow` | `Editor/` | EventChain 可视化编辑器 |
| `CharmEffectDrawer` | `Editor/Equipment/` | 护符效果自定义 PropertyDrawer |
| `MapRoomDataEditor` | `Editor/Map/` | 地图房间数据编辑器 |
| `SceneScaffoldTools` | `Editor/` | 场景脚手架快捷工具 |
| `NavSurfaceBakeShortcut` | `Editor/` | 导航网格一键烘焙快捷键 |
| `CreateEventChannelAssets` | `Editor/` | 一键创建事件频道 SO 资产菜单 |
| `ScriptExecutionOrderTools` | `Editor/` | 执行顺序可视化管理 |
| `DestructibleTileEditor` | `Editor/World/` | 可破坏瓦片编辑器 |
| `AchievementSOEditor` | `Editor/Achievements/` | 成就 SO 自定义编辑器 |
### 4.2 Inspector 设计(✅ 优秀)
- 所有配置使用 `[Header]` 分组,字段有 `[Tooltip]`
- 所有事件频道 SO 有 `description` 字段(设计师可见注释)
- `[DefaultExecutionOrder]` 系统范围覆盖(-2000 到 +50执行顺序文档化在代码中
- `[RequireComponent]` 保证依赖完整性,避免配置错误
### 4.3 潜在改进(⚠️)
| 问题 | 说明 |
|------|------|
| `HurtBoxEditor` 用反射读取私有字段 | 字段重命名后 Editor 静默失效,建议改用 SerializedProperty 或公开只读属性 |
| `SOValidationRunner` 错误检测靠关键字 "必须" / "❌" | 语言切换后可能失效,建议改为 `ValidationResult` 枚举Error / Warning / Info |
| `BatchLOSSystem` 无 Editor Gizmo | 调试时无法可视化射线,建议添加 OnDrawGizmos |
| `EventChainEditorWindow` 无截图/文档 | 新成员上手曲线较高 |
---
## 五、使用便利性8.0 / 10
### 5.1 订阅模式(✅ 优秀)
```csharp
// 组合式OnDisable 一行清理,零泄漏
private readonly CompositeDisposable _subs = new();
private void OnEnable()
{
_onPlayerSpawned.Subscribe(OnPlayerSpawned).AddTo(_subs);
_onBossFightEnded.Subscribe(OnBossEnded).AddTo(_subs);
}
private void OnDisable() => _subs.Clear();
```
相比裸 `+=/-=` 订阅,极大降低事件泄漏风险,与商业级 Rx 风格一致。
### 5.2 伤害构造(✅ 优秀)
```csharp
// 热路径首选:零分配
DamageInfo info = DamageInfo.From(source);
// 复杂流水线Builder
DamageInfo info = new DamageInfo.Builder()
.SetRaw(100).SetFlags(DamageFlags.CanBeParried | DamageFlags.CanClash)
.SetKnockback(Vector2.right, 10f).Build();
```
双模式清晰区分使用场景,符合 API 设计最佳实践。
### 5.3 状态类便捷属性(✅ 良好)
`PlayerStateBase` 提供所有常用属性的简称:
```csharp
// 在任意状态中直接使用
Anim.Play(AnimCfg.Run);
Move.Jump();
Stats.TakeDamage(info);
Buffer.Consume(InputType.Jump);
```
避免重复的 `_owner.` 链式访问,代码可读性接近 Celeste 的 `Player.cs`
### 5.4 Null-Object 模式(✅ 良好)
```csharp
NullAudioService // IAudioService 空实现Log 警告,不崩溃)
NullPlatformService // IPlatformService 空实现PC 非 Steam 环境)
NullPathAgent // IPathAgent 空实现(无导航组件时使用)
```
三个 NullObject 防御了三类常见 NullReferenceException符合商业游戏的防御性编程要求。
### 5.5 命名一致性问题(⚠️)
| 问题 | 对比 |
|------|------|
| 玩家用 `TransitionTo()` 转换状态 | 敌人用 `ForceState()` |
| 玩家用 `GetState<T>()` 取状态对象 | 敌人用 `_stateObjs[enumKey]` |
| `ServiceLocator.Get<T>()` 失败抛异常 | `GetOrDefault<T>()` 失败返回 null | (这对是有意为之,但文档化不足)|
| `Register()` 覆盖已有注册 | `RegisterIfAbsent()` 不覆盖 | (语义差异明确,但命名可更直白:`RegisterOrReplace` / `RegisterOnce` |
### 5.6 ISaveable 手动注册(⚠️)
```csharp
// SavePoint.cs, EquipmentManager.cs, AchievementManager.cs ... 各自手动调用
SaveManager.Instance.Register(this); // OnEnable
SaveManager.Instance.Unregister(this); // OnDisable
```
约有 8+ 个 ISaveable 实现重复此样板代码。商业实践(如 Unity 官方 Open Project通常用 `SaveManager.FindAndRegister<ISaveable>()` 或 ScriptableObject 驱动的注册表统一管理。
---
## 六、专项模块深度评估
### 6.1 存档系统8.5/10
**优势**
- Newtonsoft.Json 序列化(完整类型支持,无反射限制)
- `SaveMigrator.Migrate()` 版本迁移管道(向前兼容)
- `Checksum` 完整性验证(防止文件损坏导致存档不可用)
- `SemaphoreSlim` 防并发写入
- `CrashReporter` + `EmergencySaveService` 崩溃保护
- `LocalFileStorage` 通过 `ISaveStorage` 接口可替换(云存档、主机平台扩展点)
**不足**
- `SaveManager.Instance` 仍被 `DeathRespawnService` 直接引用(应通过 `ISaveService`
- `SaveData` 结构若需新字段,`SaveMigrator` 需手动更新(无自动 Schema 演化)
- 无存档文件加密(对 PC 存档修改作弊无防御,可接受)
### 6.2 输入系统8.0/10
**优势**
- `InputReaderSO` 封装 Unity InputSystem作为 ScriptableObject 可在不同场景共享
- `OnEnable` 重置防止 Play Mode 再进入时状态残留(工程实践亮点)
- `EnableGameplayInput()` / `EnableUIInput()` 提供明确的上下文切换
- `InputBuffer` 缓冲近端输入解决游戏手柄操作的时序问题Coyote Time 协同)
- `ConflictDetector` 检测按键冲突(重映射安全保障)
**不足**
- `InputReaderSO` C# 事件(非 SO 事件频道)与其他模块的 SO Channel 模式不一致——需订阅 C# event 而非通过 SO 引用
- `MoveInput` 轮询属性与事件订阅模式并存(两种取值方式)
### 6.3 摄像机系统7.5/10
**优势**
- `ICameraService` 接口隔离,`RoomCamera` / `CameraStateController` 通过服务层解耦
- `CameraBlendProfileSO` 数据驱动过渡曲线
**不足**
- `Camera/_Placeholder.cs` 说明摄像机系统尚未完整实现
- `CameraStateController` 骨架代码较多,实际行为有限
### 6.4 AI 系统8.5/10
**优势**
- BD 自定义任务节点18 个)覆盖完整 AI 行为集
- `#if GRAPH_DESIGNER` 编译守卫,无 Behavior Designer 时代码仍可编译
- `SharedString TargetStateName`vs 原 `SharedInt`枚举字符串绑定BD 图重排枚举不破坏
- `BatchLOSSystem` + `ILOSRequester` 接口:视线检测完全与敌人类型解耦
- `BD_WaitForAnimation` 使用 Animancer State 轮询而非硬编码等待时间
**不足**
- BD 图中的 `BlackboardVariable``EnemyBase` 属性之间的映射文档化不足
- `SetAggroTickRate()` 为空方法Opsive 运行时 API 变更留存了兼容桩)
### 6.5 战斗系统9.0/10
**优势**
- `DamageInfo` struct 流水线RawDamage → Amount护盾修改 → FinalDamage防御减免—— 清晰的三段式
- `DamageFlags` / `DamageTags` 位域枚举单值携带多语义CanBeParried | IgnoreIFrame
- `HurtBox` 8步流水线顺序固定无敌帧 → 弹反 → 霸体 → 护盾 → 防御减免 → TakeDamage → 广播 → DoT
- `ClashResolver` 拼刀碰撞检测独立为组件,不污染 `HitBox`
- `ParrySystem` 仅暴露窗口状态(`ConsumeParry()`),不引用玩家具体类型
**不足**
- `HitBox` 无法限制同一帧对同一 HurtBox 的多次触发(需 `_hitCooldown` + HashSet 去重)——当前 `_hitCooldown` 仅是全局冷却,多目标情况下可能误伤
- `PoiseWindowConfig` 存在但 `PlayerController.GetCurrentPoiseLevel()` 固定返回 `PoiseLevel.None`(未实现)
---
## 七、优先修复建议(按影响面排序)
### P1影响正确性
1. **EnemyBase.TakeDamage() 霸体判断 TODO**
- 现状Stagger 触发 hardcodePOCO 路径不完整
- 修复:在 `TakeDamage()` 中根据 `DamageInfo.Break` 和当前霸体等级选择 `Stagger``Hurt`
2. **HitBox 同目标重入保护**
- 现状:`_hitCooldown` 仅限制全局频率,多目标情况下可能一帧命中同一 HurtBox 多次
- 修复:维护 `HashSet<Collider2D> _hitThisActivation``Deactivate()` 时清空
3. **ISaveable 自动注册**
- 现状8+ 个实现类手动 Register/Unregister
- 修复:在 `SaveManager.Awake()``FindObjectsOfType<ISaveable>()` 批量注册(允许一次 FindObjects
### P2影响质量
4. **移除 AudioManager.Instance 单例**
- 仅通过 `ServiceLocator.Get<IAudioService>()` 访问
5. **StatusEffectManager.CreateEffect() 替换 switch**
- 改为 `[SerializeField] private StatusEffectMappingSO _mapping`,设计师可配置 DamageType→Effect
6. **AntiSoftlockSystem 加入命名空间**
- 当前无 namespace建议 `namespace BaseGames.Support.AntiSoftlock`
7. **BatchLOSSystem 删除冗余 _results List**
- 结果已通过回调传递,`_results` 字段仅占内存,删除即可
### P3体验优化
8. **HurtBoxEditor 改用 SerializedProperty**
- 避免反射字段名依赖,防止重命名导致 Editor 静默失效
9. **SOValidationRunner 使用枚举结果**
- `ValidationResult` { Error, Warning } 代替关键字字符串匹配
10. **VFXPool.Play() 同步取池**
- 池命中时跳过协程,直接同步设置 Transform 并播放
11. **完成 AudioEventSO Phase 2 集成**
- `PlayBGM(string)` / `PlaySFX(string)` 目前输出警告,应接入 AudioEventSO 资产查找
---
## 八、对标商业游戏评估
| 维度 | Hollow Knight 类比 | 本项目水准 |
|------|------------------|-----------|
| 模块隔离 | 单 Assembly早期 | ✅ 25+ asmdef更现代 |
| 事件解耦 | C# event 直连 | ✅ SO EventChannel更可配置 |
| 存档系统 | Binary 格式 | ✅ JSON + 迁移器,更易维护 |
| 对象池 | 自定义池 | ✅ Addressables + LRU更完整 |
| 编辑器工具 | 无 | ✅ 16 个专用工具,远超同类 |
| AI 调试 | 无 | ✅ EventBusMonitor + HurtBoxEditor |
| 状态机 | MonoBehaviour 继承 | ✅ POCO 状态对象,更轻量 |
| 单元测试 | 无 | ⚠️ `ServiceLocator.Reset()` 提供基础,但测试文件尚未建立 |
---
## 九、总结
本代码库在 **Indie 游戏**中属于**顶层水准**,在架构规范性、模块化程度和编辑器工具链方面已达到部分 **AA 商业标准**。核心机制(战斗流水线、状态机、存档系统、对象池)设计扎实,接口边界清晰,后续扩展成本低。
主要短板集中在**三个方面**
1. **遗留单例模式**7 处)与 ServiceLocator 并存——形成双入口访问隐患
2. **数个模块处于 Phase 1 / Stub 阶段**Audio Phase 2、霸体判断、IEnemyState Phase 2
3. **缺乏自动化测试覆盖**——ServiceLocator 测试基础设施已就绪,但测试文件数量为零
若在当前基础上补齐上述三点,该代码库完全达到**独立发行商业游戏**的代码质量要求。
---
*本报告由 GitHub Copilot 自动分析生成,基于源代码静态阅读,不包含运行时 Profile 数据。*