多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -0,0 +1,546 @@
# 泽灵 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 数据。*