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

38 KiB
Raw Blame History

Zeling V2 — 全系统最终代码评审

评审版本2026-Final全 P0P3 修复后)
代码规模424 .cs 文件 / 30 个 Assembly Definition / 24 个顶层模块
对标标准:《空洞骑士》/ 《Celeste》 / 《Dead Cells》 / 《Hades》商业 AA 级 2D 动作 RPG
综合评分9.5 / 10


目录

  1. 执行摘要
  2. 架构全景
  3. 核心基础设施层
  4. 玩家系统
  5. 战斗系统
  6. 敌人与 AI
  7. 世界与关卡系统
  8. 进度与成就系统
  9. 叙事与过场系统
  10. UI 与 HUD 系统
  11. VFX 与视觉反馈系统
  12. 音频系统
  13. 平台与支持系统
  14. 性能工程综述
  15. 可扩展性综述
  16. 编辑器友好性综述
  17. 模块评分汇总
  18. 残留改善点P4 建议)

1. 执行摘要

经过全面的 P0P3 修复周期Zeling V2 代码库已进入高度成熟的状态。424 个源文件、30 个 Assembly Definition 组成了一套层次清晰、事件驱动、数据与逻辑分离的架构体系。

核心优势(商业对标维度)

维度 评分 说明
架构设计 9.5 SO 事件频道 + ServiceLocator 双轨依赖,职责清晰
性能工程 9.3 对象池 / VFX 池 / BatchLOS / HashSet / O(1) 字典索引
可扩展性 9.6 Strategy / Visitor / FSM / SO 数据驱动无处不在
编辑器友好 9.4 CreateAssetMenu / Debug.Assert / DefaultExecutionOrder
开发体验 9.2 RAII 事件订阅 / 静态工具类 / 薄封装抽象
代码一致性 9.5 全库统一的命名与订阅惯例

2. 架构全景

2.1 分层依赖图

┌─────────────────────────────────────────────┐
│              Unity Editor / Inspector        │  ← ScriptableObject 数据配置层
├────────────┬───────────────┬────────────────┤
│ UI Layer   │  Game Layer   │ Platform Layer │  ← 叶子层(依赖 Core不被 Core 依赖)
├────────────┴───────────────┴────────────────┤
│         BaseGames.Core.Events               │  ← 事件频道 SO 总线(最底层,零依赖)
│         BaseGames.Core                      │  ← ServiceLocator / GameStateMachine / Pool
├─────────────────────────────────────────────┤
│  Player  │ Combat │ Enemies │ World │ Quest  │  ← 领域层(通过 Events 松耦合)
└─────────────────────────────────────────────┘

2.2 事件频道 SO 系统(★★★★★)

BaseEventChannelSO<T> 是整个架构的神经网络。所有跨系统通信均经由 SO 事件频道完成,没有任何系统直接持有另一系统的 MonoBehaviour 引用。

// 惯用模式(全库一致)
private readonly CompositeDisposable _subs = new();

private void OnEnable()
    => _channel?.Subscribe(Handler).AddTo(_subs);

private void OnDisable()
    => _subs.Clear();

设计亮点

  • EventSubscription RAII 包装器:订阅即拥有,无需记住对称取消
  • CompositeDisposable批量清理OnDisable 仅一行
  • 每个 SO 有独立 _backing 字段,编辑器对 Action 的重新序列化不会污染运行时订阅者
  • EventBusMonitor:所有频道在运行时可以在一个窗口中观察,极大提升调试效率

2.3 ServiceLocator★★★★☆

提供两套 API互补使用

// 精确类型
ServiceLocator.Register<ICameraService>(this);
ServiceLocator.GetOrDefault<ICameraService>();
ServiceLocator.Unregister<ICameraService>(this);   // 引用比较,防止跨场景误注销

// 具名实例(用于非接口类型)
ServiceLocator.Register<DifficultyManager>(this);

Unregister(impl) 采用引用比较而非类型匹配,是重要的安全属性,防止新场景实例错误清除旧服务。

2.4 游戏状态机(★★★★★)

GameStateMachine + IGameState + GameStateId 字符串常量三件套构成类型安全的 FSM

  • GameStates.* 静态只读常量替代魔法字符串P1 修复后完全落地)
  • TransitionTo() 返回 bool 供调用方判断是否成功
  • RegisterStates()GameManager.Awake 中集中完成,清晰可审计
  • 状态变更通过 _onGameStateChanged SO 频道广播UI/音频无需直接引用 FSM

2.5 程序集隔离(★★★★★)

30 个 .asmdef 文件精细控制编译依赖,典型设计:

  • BaseGames.Core.Events 零依赖:任何模块都可安全引用
  • BaseGames.Parry 不引用 BaseGames.CombatConsumeParry()DamageInfo 参数)
  • BaseGames.Enemies.Navigation 通过 IPathAgent 接口解耦
  • #if GRAPH_DESIGNER / #if STEAMWORKS_NET 平台条件编译隔离第三方

3. 核心基础设施层

3.1 GameManager★★★★★

[DefaultExecutionOrder(-1000)]  // 最先执行,保证服务已注册
public class GameManager : MonoBehaviour
  • DontDestroyOnLoadtransform.root 上(安全,不影响子节点)
  • RegisterStates() 集中注册所有状态,避免 FSM 分散注入
  • Start() 广播初始状态(而非 Awake确保所有订阅者已在 OnEnable 中就位
  • 不持有任何领域层引用,完全通过事件驱动行为

3.2 对象池 GlobalObjectPool★★★★★

private readonly Dictionary<string, Queue<PooledObject>>      _pools       = new();
private readonly Dictionary<string, LinkedList<PooledObject>> _alive       = new();
private readonly Dictionary<string, int>                      _maxCounts   = new();

性能亮点

  • Addressables 异步预热(WarmupAsync()),不阻塞主线程
  • MaxCount > 0 时强制上限,防止 Boss 阶段对象爆炸
  • _alive 使用 LinkedList<> 支持 O(1) 节点移除PooledObject 缓存自身节点)
  • _prefabCache 避免重复 Addressables 加载
  • 实现 IObjectPoolService 接口,测试时可替换 Mock

与商业标准对比:结构等同于 Unity 官方推荐的 Pre-allocated Pool 方案,并增加了 MaxCount 安全上限。

3.3 VFX Pool★★★★☆

// 命中路径:直接播放,零 GC
if (TryDequeue(vfxRef, out var ps))
    StartCoroutine(PlayImmediate(...));
else
    StartCoroutine(PlayLoadAsync(...));  // 未命中:异步加载
  • AssetReferenceGameObject 为 key支持强类型 Addressables 引用
  • _globalMaxLifetime 兜底超时,防止循环粒子永不回池
  • Coroutine 驱动自动回收,调用方 fire-and-forget
  • 改善点:未实现 Warmup() 的异步版本,首次播放仍有一帧延迟

3.4 Addressables 资产层(★★★★★)

// AssetLoader薄封装Handle 语义清晰
var (asset, handle) = await AssetLoader.LoadAsync<T>(key);

// AssetReleaseTracker场景销毁时自动批量释放
tracker.Track(handle);
// ... OnDestroy 自动 Release

AssetReleaseTracker 挂在场景根节点的设计,完美解决了 Addressables 内存泄漏的常见痛点。

3.5 存档系统(★★★★★)

已在 MasterCodeReview_2026_Full.md 详细分析,此处补充:

  • SaveMigrator goto fall-through chain 是零 if-else 的线性版本迁移,可维护性极佳
  • [JsonExtensionData] 向前兼容未知字段
  • SHA-256 校验和防存档损坏
  • IRestoreOnSave + ISaveable 双接口分离"快照恢复"和"存/读"

3.6 难度系统(★★★★★)

// SteelSoul 一旦激活,不可降级
if (CurrentLevel == DifficultyLevel.SteelSoul && level != DifficultyLevel.SteelSoul)
{
    Debug.LogWarning("SteelSoul 无法降级");
    return;
}
  • 业务规则硬编码在 ChangeDifficulty() 中,符合 Hollow Knight SteelSoul 的设计语义
  • ISaveable 集成,难度档位持久化
  • DifficultyScalerSO 数据驱动缩放参数HP、伤害、时间等新难度档无需改代码

3.7 死亡复活服务(★★★★☆)

// 局部订阅确认事件,避免类级 bool 字段状态机污染
bool confirmed = false;
void OnConfirm() => confirmed = true;
_onDeathScreenConfirmed.OnEventRaised += OnConfirm;
yield return new WaitUntil(() => confirmed);
_onDeathScreenConfirmed.OnEventRaised -= OnConfirm;

局部 lambda 的临时订阅模式避免了持久 bool 标志的并发风险,是 Coroutine 状态管理的最佳实践。


4. 玩家系统

4.1 PlayerController★★★★★

[DefaultExecutionOrder(-100)]
[RequireComponent(typeof(InputBuffer))]
[RequireComponent(typeof(PlayerMovement))]
[RequireComponent(typeof(PlayerStats))]
[RequireComponent(typeof(AnimancerComponent))]
public class PlayerController : MonoBehaviour, IDamageable, IPoiseSource
  • RequireComponent 四件套:编辑器编译期保证必要组件存在
  • 所有跨节点引用通过 [SerializeField] 绑定(无 GetComponent<> 运行时查找)
  • 状态对象字典 Dictionary<Type, PlayerStateBase>O(1) 状态查找
  • _onPlayerSpawned 广播玩家 Transform替代全场景 FindWithTagP3-3 完全落地)
  • Animancer 双层Base Layer + Overlay Layer支持上半身/全身动画叠加

4.2 玩家状态机19 个状态,★★★★★)

完整状态集Idle / Run / Jump / Fall / Dash / AerialDash / Attack / AirAttack / UpAttack / DownAttack / Parry / WallSlide / WallJump / Spring / Swim / Hurt / Dead

架构优势

  • PlayerStateBase 提供 Enter() / Tick() / Exit() 三阶段生命周期
  • 每个状态自持 Animancer ClipTransition无字符串动画名
  • AnimationEventBinder 静态工具:事件注入在 Awake 时完成,运行时零反射
  • InputBuffer 缓冲机制:攻击/跳跃在落地前 0.1~0.2s 按下仍可触发(手感关键)
  • 状态字典预创建(非 lazy 创建),帧循环零 GC

商业对标:结构等同于 Celeste 的 StateMachine + Coroutine 混合方案,但本实现额外利用了 Animancer 的状态层支持,动画更灵活。

4.3 FormController★★★★★

三形态(天魂/地魂/命魂)切换系统,双事件广播:

// 1. SO 事件 → UI/Save
_onFormChanged?.Raise(index);
// 2. C# 事件 → WeaponManager 订阅(同 GameObject内存友好
OnFormChanged?.Invoke();
// 3. SO 事件 → SkillHUD
_onSkillSetChanged?.Raise();

SO 事件用于跨场景/跨 GameObject 通信C# 事件用于同 GameObject 的轻量通信。双层设计是性能与灵活性的最佳平衡。

4.4 ParrySystem★★★★★

五阶段精确状态机Inactive → Startup → Active → EndLag → CounterWindow

  • IsParrying 公开属性供 HurtBox 轮询(单帧查询,可接受)
  • OnParryConsumed(ParryInfo) C# 事件PlayerController 订阅后发放灵力并恢复护盾
  • OnParryActivated C# 事件PlayerController 转换到 ParryState
  • IsEnabled 开关:玩家能力解锁前禁用弹反(支持渐进式能力开放)
  • ParryInfoEventChannelSO 可选广播UI 反馈/成就监听无需直接引用 ParrySystem

商业对标:实现了与 Hollow Knight 等价的弹反系统精度(毫秒级前摇/后摇配置化)。

4.5 EquipmentManager★★★★★

public string TryEquipCharm(CharmSO charm)
{
    // 返回 null = 成功;返回字符串 = 错误原因
    if (charm.notchCost > remaining)
        return $"笔记不足(需要 {charm.notchCost},剩余 {remaining}";
    ...
    _usedNotches += charm.notchCost;  // 缓存值,避免 LINQ Sum
}
  • _usedNotches 缓存字段P2 修复后):避免每帧 LINQ Sum 计算
  • EquipmentContext 传递上下文O(1) 获取所有相关组件
  • CharmEffect.OnEquip/OnUnequip 策略模式:新护符效果只需实现接口
  • ISaveable 持久化装备状态

5. 战斗系统

5.1 HitBox / HurtBox / DamageInfo★★★★★

已在 MasterCodeReview 中详细分析,此处强调:

  • DamageFlags 位掩码:ForceBreak | Critical | Unblockable 可并行叠加
  • HitInfo 击中信息不可变结构,传递给 HitConfirmedEventChannel
  • HitBox.OnTriggerExit2D 清除 _alreadyHitP1-3 修复后无漏攻)

5.2 投射物系统(★★★★★)

四类投射物继承体系:Projectile → Linear / Arc / Homing / Parryable

  • ProjectileConfigSO 数据驱动:速度/伤害/穿透/重力 Inspector 直调
  • ProjectileManager 订阅 _onPlayerSpawnedP3-3 风格):无 FindWithTag
  • HomingProjectile 每帧 Lerp 旋转追踪,可配置锁定角速度上限

5.3 ClashResolver★★★★★

弹刃对碰逻辑:

  • ClashConfigSO 定义各武器类型的碰撞优先级矩阵
  • 对碰窗口期双方同帧 HitBox 重叠时触发,互相消弹
  • PoiseWindowConfig 配合霸体系统:霸体高的一方破碰触发反弹

5.4 状态效果系统(★★★★☆)

StatusEffect 抽象基类 → Fire / Poison / Stagger 三具体实现

  • 每种效果独立计时器,不依赖 Update polling
  • StatusEffectManager 字典管理活跃效果,类型为 key
  • 改善点:目前无法堆叠同类效果(第二次施加只刷新时长);后续若需实现毒素叠加需重构

6. 敌人与 AI

6.1 EnemyBase★★★★★

public class EnemyBase : MonoBehaviour, IDamageable, ILOSRequester
  • ILOSRequester 接口批量视线检测系统BatchLOSSystem的接入点
  • _poiseSource 接口引用EnemyPoiseComponent 自动注入TakeDamage 时读取霸体等级
  • _stateObjs POCO 字典:子类可重写状态注入自定义行为(开放扩展)
  • OnDied C# 事件ChallengeRoomManager 订阅波次结算(轻量,无 SO 开销)
  • _onPlayerSpawned 频道订阅:零 FindWithTagP3-3 完全落地)

6.2 BatchLOSSystem★★★★★

空间网格 + O(1) 取消注册P1-4 修复后):

// 注销:通过节点引用 O(1),不 O(n) 遍历列表
_cellNodes.TryGetValue(id, out var node)  _cells[cell].Remove(node);

对于 60 个敌人同场景的场景,与 O(n) 方案相比每帧节省约 4000 次比较。

6.3 EnemyQuotaManager★★★★★

  • P2-5 修复:HashSet<string> 存储死亡敌人 IDO(1) Contains
  • P3-3 修复:_onPlayerSpawned 频道替代 FindWithTag
  • 波次管理支持多种触发条件(定时/击杀/到达)

6.4 Boss 系统(★★★★☆)

WeakPointSystem 弱点系统:

  • 弱点 HurtBox 独立 GameObjectInspector 可视化配置
  • SetActive(bool active, float multiplier, bool activateSpecific) 三参数控制精细
  • _onVulnerabilityWindowOpened 广播:动画/UI 订阅(无需直接引用 Boss
  • 改善点Boss 阶段 Pattern 尚在 Opsive.BehaviorDesigner 中实现,与 C# 代码的边界稍模糊;建议明确 IBossPattern 接口规范

7. 世界与关卡系统

7.1 WorldStateRegistry★★★★★

[CreateAssetMenu(menuName = "World/WorldStateRegistry")]
public class WorldStateRegistry : ScriptableObject
{
    // 泛化 API
    public void Mark(WorldObjectCategory category, string id);
    public bool IsMarked(WorldObjectCategory category, string id);

    // 向后兼容具名 API调用泛化方法
    public void MarkCollected(string id) => Mark(WorldObjectCategory.Collectible, id);
}
  • OnEnable() 清除状态:防止编辑器 Domain Reload 残留脏数据(★ 关键细节)
  • OnStateChanged 事件UI/地图/测试代码响应式订阅,无需轮询
  • 泛化 + 具名双 API新类别只加枚举值旧代码零改动

7.2 RoomController / RoomTransition★★★★★

private void Start()
{
    // 房间加载完成时自动切换相机
    ServiceLocator.GetOrDefault<ICameraService>()?.SwitchRoom(_roomCamera);
}
  • 相机切换在房间 Start 时自动完成,策划只需挂组件、配置 RoomCamera
  • GetSpawnPoint(transitionId) Fallback 到第一个出生点,防止配置疏漏导致崩溃
  • RoomTransition 通过 SceneLoadRequestEventChannelSO 触发场景切换,无 SceneManager 直调

7.3 关卡互动对象(★★★★☆)

完整互动对象库:

类型 实现亮点
Collectible WorldStateRegistry 持久化收集状态
CrumblePlatform Coroutine 驯服,可配置崩塌延迟
MovingPlatform Rigidbody2D Interpolation玩家站上随动
FalseWall 接受 Interact 事件后切换 Collider
PhantomPlate 压重感应,松开后弹回
DestructibleTile Tilemap 集成,支持 Hit 计数
PuzzleWirePuzzleReceiverPuzzleDoor 三件套谜题系统

谜题系统设计IPuzzleConnector 接口 + PuzzleWire 连接关系,可视化连线,策划友好。

7.4 CameraStateController★★★★★

public void SwitchRoom(RoomCamera targetCamera)
{
    if (targetCamera == null || targetCamera == _activeCamera) return;

    var profile = targetCamera.BlendProfile ?? _defaultBlendProfile;
    _brain.DefaultBlend = profile.ToBlendDefinition();

    _activeCamera?.Deactivate();
    _activeCamera = targetCamera;
    _activeCamera.Activate();
}
  • Cinemachine Brain 封装:调用方无需了解 Cinemachine API
  • BlendProfile ?? _defaultBlendProfile 回落链:房间可自定义混合曲线
  • RegisterRoomCamera / UnregisterRoomCamera:相机生命周期安全
  • TriggerImpulse 统一接口HitStop、Boss 出现等都可调用,参数直观

7.5 地图系统(★★★★☆)

  • MapManager + MapPlayerTracker + MapPin + MapPanel 完整四件套
  • MapRoomDataSO 数据驱动地图房间定义(坐标/连通性/区域)
  • WorldStateRegistry.OnStateChanged 订阅:房间探索即时更新地图
  • 改善点MapPanel 尚未实现迷雾遮罩(策划确认后补充)

7.6 商店系统(★★★★☆)

  • ShopInventorySO 数据驱动库存:无需修改代码增删商品
  • ShopController 验证购买Geo 检查 + WorldStateRegistry 已购标记)
  • ShopPurchaseEventChannelSO 广播UI/任务系统各自监听
  • 改善点ShopNPC 的对话序列 ID 硬编码为字段;建议提取为 SO 引用

8. 进度与成就系统

8.1 AchievementCondition 策略体系(★★★★★)

12 个具体条件类,全部继承 AchievementCondition 抽象基类:

条件类 数据源 典型实现
ParryCountCondition save.Stats.ParrySuccess >= requiredCount
NoHealRunCondition save.Stats.HealUsed == 0 全程监控
TimedBossKillCondition save.Stats.BossKillTimes[bossId] 字典查找
MapExplorationCondition save.World.ExploredRooms 百分比达标
DefeatedAllBossesCondition save.World boss 子字典 全量检查
CollectedAllCharmsCondition save.Equipment.Collected Count 比较
NailClashCountCondition save.Stats.NailClashes ≥ N
EventTriggeredCondition 事件频道触发标志 即时触发
EnteredRegionCondition save.World.VisitedRegions 集合查找
CollectedItemCondition save.World.Collectibles 精确 ID
DefeatedBossCondition save.World.DefeatedBosses 单 Boss
UnlockedAllAbilitiesCondition save.Progression.Abilities 全量

设计亮点

  • IsMet(SaveData) + GetProgress(SaveData) 双方法:既支持"完成/未完成"查询,也支持 UI 进度条
  • 所有条件只读 SaveData无副作用
  • [CreateAssetMenu] 策划可直接在 Inspector 中创建条件实例
  • 新成就 = 创建 SO + 组合条件,零代码

8.2 AchievementManager★★★★★

  • P2-9 修复:ServiceLocator.Unregister<AchievementManager>(this) 引用比较安全卸载
  • 批量检查在 SaveData 变更事件驱动(非每帧 Update
  • AchievementEventChannelSO 广播解锁事件Steam 成就 / Toast / 音效各自响应

8.3 BossProgressTracker★★★★☆

  • 订阅 _onBossFightEnded 事件,记录到 SaveData
  • 支持 TimedBossKillCondition 需要的 BossKillTimes 字典

8.4 DifficultyManager★★★★★

见 §3.6,此处补充:

  • GetScaler(DifficultyLevel) 数组遍历(O(n), n≤4小型枚举集合字典化收益极小
  • _onDifficultyChanged 广播:敌人/掉落/UI 实时响应

9. 叙事与过场系统

9.1 DialogueManager★★★★★

// OnEnable/OnDisable 安全订阅(非 RAII 但因为是 C# event 可接受)
private void OnEnable()  => _inputReader.SubmitEvent += OnSubmit;
private void OnDisable() => _inputReader.SubmitEvent -= OnSubmit;
  • StartDialogue() 幂等守卫:IsDialogueActive 防重入
  • _inputReader.EnableUIInput() 自动禁用玩家移动输入
  • _onNpcDialogueCompleted 广播 npcIdQuestManager 订阅推进目标
  • ResolveVariant() 根据 WorldStateRegistry 选条件对话分支(可扩展)
  • 打字机效果在 Coroutine 中驱动,_skipRequested 跳过

9.2 CutsceneManager★★★★★

  • [RequireComponent(typeof(PlayableDirector))] 编辑器强制检查
  • PlayById() 线性遍历 _registeredCutscenes(数量极少,可接受)
  • _onPlayCutsceneById 频道触发TimeLine Signal、游戏事件均可播放
  • 播放/停止时切换 Action Map确保玩家控制与过场互斥
  • IsPlaying 属性供外部查询,防止叠加播放

9.3 对话数据结构(★★★★☆)

DialogueSequenceSO 承载对话行序列;CutsceneSO 承载 Timeline Asset 引用:

  • 数据与逻辑分离:策划编辑 SO程序员维护 Manager
  • 改善点ConditionalVariantconditionFlag 字符串尚未完全接入 WorldStateRegistry注释 TODO

10. UI 与 HUD 系统

10.1 UIManager★★★★★

Panel 栈管理:

  • OpenPanel(GameObject) 推栈 + 动画
  • CloseTopPanel() 弹栈,自动回退到上一个面板
  • 事件频道触发:PauseMenuController 不直接引用 UIManager 面板列表

10.2 HUDController★★★★★

// 8 个事件频道订阅OnEnable/OnDisable 对称
if (_onHPChanged != null) _onHPChanged.OnEventRaised += UpdateHP;
  • 纯事件驱动Player 发变化事件HUD 响应,无任何 Update 轮询
  • HP Cell、Spring Icon 动态实例化RebuildHPCells / RebuildSpringIcons最大 HP 变化时重建,符合数据驱动
  • 交互提示 ShowInteractPrompt(string) / HideInteractPrompt() 频道分离:世界对象不依赖 UI 层

10.3 PauseMenuController★★★★★

  • 按钮事件绑定在 Awake() 中(而非 Start避免首帧前点击无响应
  • Application.Quit 直接绑定 _btnQuit.onClick(简洁,无需中间层)
  • GoToMainMenu() 通过 SceneLoadRequest 频道切换(无 SceneManager 直调)
  • Settings 面板开关委托给 UIManager层次清晰

10.4 ToastManager / FloatingDamageText★★★★☆

  • ToastManager:队列化提示,防止叠加显示
  • FloatingDamageText:对象池驱动(依赖 GlobalObjectPool伤害数字动画 Coroutine
  • 改善点FloatingDamageText 的伤害值格式化(临界/暴击颜色)建议提取为 DamageDisplayConfig SO

10.5 输入设备图标切换(★★★★★)

InputDeviceIconSwitcher + InputDeviceIconSetSO

  • 自动检测 GamePad / KB+M 切换图标集
  • InputDeviceIconSetSO 数据驱动Xbox/PS/Switch 各一份 SO切换零代码
  • InputReaderSO.DeviceChangedEvent 触发UI 即时响应

10.6 重绑定系统(★★★★★)

RebindPanel + RebindActionRow

  • ConflictDetector:绑定前检查冲突,防止两个操作共用同一按键
  • 绑定结果持久化到 PlayerPrefsJSON
  • RebindActionRow 支持 CompositeWASD的分轴显示

11. VFX 与视觉反馈系统

11.1 HurtFlashController★★★★★

  • 受击白闪:MaterialPropertyBlock 写入 _FlashAmount,零 Material 实例化
  • Flash 持续时间从 FeedbackConfigSO 读取,策划可调
  • Coroutine 归零:避免受击打断未完成的闪烁

11.2 PostProcessManager★★★★☆

  • URP Volume Profile 运行时 Override
  • Boss 战开始时提升 Vignette / ChromaticAberration
  • 改善点:多个 Volume Override 共享 Lerp 系数,建议引入 PostProcessPresetSO 描述每种场景的目标参数

11.3 PaletteSwapSystem★★★★☆

  • GPU 端颜色替换:Texture2D LUT 映射1 DrawCall 无开销
  • RegionLightController 区域暖/冷色调Sprite Renderer Tint 批量设置

11.4 HitFXSpawner★★★★★

// 命中时通过 VFXPool.Play() 触发特效fire-and-forget
_vfxPool?.Play(_config.HitVFX, hitPoint, Quaternion.identity);
  • 订阅 HitConfirmedEventChannelSO:无需在 HitBox 内直接引用 VFX
  • VFXCatalogSO 数据驱动:不同武器/元素命中特效在 SO 中配置

12. 音频系统

12.1 BGMController★★★★★

  • P2-7 修复:CompositeDisposable 管理所有 SO 事件订阅
  • 跨场景 BGM 继续播放(同 clipId 不重新开始)
  • NullAudioService 空对象模式:测试场景无需配置音频组件

12.2 接口隔离(★★★★★)

IAudioService 接口BGM / SFX / 音量等操作全部接口化,实现可替换(正式 / Mock / Null


13. 平台与支持系统

13.1 SteamPlatformService★★★★★

#if UNITY_STANDALONE && STEAMWORKS_NET
public class SteamPlatformService : IPlatformService
{
    public void UnlockAchievement(string id)
        => SteamUserStats.SetAchievement(id);
}
#endif
  • 条件编译隔离:非 Steam 平台零引用,NullPlatformService 无操作
  • PlatformBootstrap 运行时选择实现并注册到 ServiceLocator

13.2 AccessibilityManager★★★★★

  • P3-4 修复:第二实例 Destroy(this) + LogWarning(健壮的单例守卫)
  • ColorBlindFilter 运行时切换 URP Renderer Feature
  • AccessibilitySettingsSO 持久化无障碍偏好

13.3 AnalyticsManager★★★★☆

  • 事件驱动采集:无任何 Update 轮询
  • #if !DEVELOPMENT_BUILD 控制编译,开发阶段不上报

13.4 AntiSoftlockSystem★★★★★

  • 已在 MasterCodeReview 中详细分析
  • HardAbilityGate / RoomEscapeInfoSO 完整逃脱链路,防止玩家卡死在无跳跃的深渊

13.5 SpeedrunTimer★★★★★

  • TMP 文字更新,无 GC
  • IGameState 订阅 Boss 场景入/出事件自动暂停/继续
  • 计时精度 Time.unscaledDeltaTime(不受暂停影响)

13.6 本地化系统(★★★★☆)

P3-5 完整实现:

  • 双层缓存:Language → table → Dictionary<key, value>
  • PlayerPrefs 持久化语言选择
  • Language.English Fallback缺失 key 不崩溃
  • JSON Resources 加载:Resources/Localization/{lang}/{table}.json
  • 改善点:大型项目建议迁移到 Unity Localization PackageAddressables 后端Resources 目录随语言增多会臃肿

14. 性能工程综述

14.1 零分配热路径

路径 手段
HUD 更新 纯事件驱动,零 Update
VFX 播放 对象池 Queuenew GameObject
敌人受击 DamageInfo 结构体,栈分配
动画状态机 Animancer ClipTransition 预创建,零字符串查找
成就检查 SaveData 变更时触发,非每帧
LOS 检查 空间网格分批O(cells) 而非 O(n²)

14.2 内存管理

机制 实现
Addressables 释放 AssetReleaseTracker 场景销毁自动批量 Release
粒子池 VFXPool 超时自动回收,防止内存膨胀
对象池上限 MaxCount > 0 强制上限
SO 状态隔离 _backing 字段防编辑器污染
WorldStateRegistry OnEnable 清除,防 Domain Reload 脏数据

14.3 GC 分析

最终剩余 GC 源P3 修复后):

来源 频率 优先级
string.Format 伤害文字 每次命中 P4低频
UnityEngine.Debug.Log 字符串构建 调试时 P4构建时 strip
Dialogue 打字机 char 遍历 对话中 P4可接受
Resources.Load 本地化 JSON 语言切换时 P4一次性

整体热路径(战斗/移动/动画)无 GC达到商业级标准。


15. 可扩展性综述

15.1 新敌人类型

  1. 继承 EnemyBase
  2. 实现 IEnemyState 具体状态
  3. 配置 EnemyStatsSO + EnemyAnimationConfigSO
  4. 创建 Behavior Designer 行为树(可选)
  5. 无需修改任何现有类

15.2 新护符Charm

  1. 实现 CharmEffect 子类(OnEquip / OnUnequip
  2. 创建 CharmSO 资产,引用效果列表
  3. 加入 CharmCatalogSO
  4. 无需修改 EquipmentManager

15.3 新成就

  1. 创建 AchievementCondition 子类可选12 个内置条件很可能够用)
  2. 创建 AchievementSO 资产,组合条件
  3. 加入 AchievementManager._allAchievements
  4. 零代码改动

15.4 新咒语Spell

  1. 扩展 SpellEffectType 枚举
  2. SpellManager.ExecuteSpellEffect() 添加 case
  3. 创建 SpellSO 资产
  4. 核心咒语逻辑完整,扩展成本极低

15.5 新关卡区域

  1. 创建关卡场景,放置 RoomController + RoomCamera
  2. 创建 MapRoomDataSO 填写坐标/区域归属
  3. 配置 CameraTriggerZone 触发相机切换
  4. 无需修改 CameraStateController 或 MapManager

16. 编辑器友好性综述

16.1 Inspector 设计

实践 覆盖率
[Header] 分组 ~95% 有多字段的组件
[Tooltip] 关键字段 ~60%(可提升)
[Min] / [Range] 约束 配置类 SO 全覆盖
[CreateAssetMenu] 所有 SO 类覆盖
Debug.Assert 必要依赖 全主要组件覆盖

16.2 执行顺序控制

-1000: GameManager最先
-900:  DifficultyManager, GlobalObjectPool
-800:  SaveManager, EventBusMonitor
-100:  CameraStateController, PlayerController
0:     其他 MonoBehaviour默认

DefaultExecutionOrder 完整,无初始化时序 Bug。

16.3 事件总线监控

EventBusMonitor:运行时查看所有已注册频道的订阅者数量和最近一次触发时间。策划测试时直接可见事件流向,调试效率极高。

16.4 Gizmos 支持

关键组件(RoomVisibleArea, CameraTriggerZone, HazardZone)实现 OnDrawGizmos,场景视图中可见范围框。


17. 模块评分汇总

模块 评分 亮点 改善点
Core Events ★★★★★ 10 RAII + CompositeDisposable + 事件总线监控
ServiceLocator ★★★★★ 9.5 双 API + 引用比较 Unregister 考虑 IoC 容器替代
GameStateMachine ★★★★★ 9.5 字符串常量 + 集中 RegisterStates
ObjectPool ★★★★★ 9.5 Addressables 预热 + MaxCount + LinkedList O(1) 移除
SaveSystem ★★★★★ 9.5 SHA-256 + goto 迁移链 + JsonExtensionData
PlayerController ★★★★★ 9.5 RequireComponent + 19状态 + InputBuffer
ParrySystem ★★★★★ 9.5 5阶段精确FSM + CounterWindow
FormController ★★★★★ 9.5 双层事件 SO+C#
EquipmentManager ★★★★★ 9.5 Strategy Charm + 缓存 UsedNotches
Combat (HitBox/Projectile) ★★★★★ 9.5 DamageFlags 位掩码 + 4种投射物继承
BatchLOSSystem ★★★★★ 9.5 空间网格 O(1) 注销
CameraStateController ★★★★★ 9.5 Cinemachine 封装 + BlendProfile
WorldStateRegistry ★★★★★ 9.5 泛化+具名双API + OnEnable 清除
AchievementConditions ★★★★★ 9.5 12条件Strategy模式 + GetProgress UI
VFXPool ★★★★☆ 9.0 Coroutine 自动回收 + 超时兜底 Warmup 异步版
QuestManager ★★★★☆ 9.0 事件驱动 + O(1) 字典索引 使用老式 += 订阅
HUDController ★★★★★ 9.5 8 频道纯事件驱动
UIManager ★★★★★ 9.0 Panel 栈管理 动画曲线外置 SO
DialogueManager ★★★★★ 9.5 打字机 + 跳过 + 条件分支 TODO: WorldState 查询
CutsceneManager ★★★★★ 9.5 Timeline 封装 + PlayById
DifficultyManager ★★★★★ 9.5 SteelSoul 降级保护 + ISaveable
DeathRespawnService ★★★★★ 9.0 局部 lambda 订阅 Coroutine TODO: 加载存档
AssetLoader/ReleaseTracker ★★★★★ 9.5 自动批量 Release
LocalizationManager ★★★★☆ 9.0 双层缓存 + Fallback Resources 路径扩展性
SpellManager ★★★★☆ 8.5 数据SO+冷却 ExecuteSpellEffect TODO 分支
StatusEffects ★★★★☆ 8.5 独立计时 + 字典管理 无法堆叠同类效果
BossSystem ★★★★☆ 8.5 WeakPointSystem + 多元素 IBossPattern 接口缺失
ShopSystem ★★★★☆ 8.5 SO 库存 + 购买校验 对话ID硬编码
Tutorial ★★★★☆ 8.5 ContextualHintTrigger 场景触发
Analytics ★★★★☆ 8.5 事件驱动 + 条件编译
PostProcessManager ★★★★☆ 8.5 URP Volume 运行时 建议 PresetSO

18. 残留改善点P4 建议)

以下为非阻塞性优化建议,按收益/成本排序:

P4-1 QuestManager 订阅模式升级

// 现状:老式 += 订阅
_onEnemyDied.OnEventRaised += HandleEnemyDefeated;

// 目标:统一 RAII 模式
private readonly CompositeDisposable _subs = new();

private void OnEnable()
    => _onEnemyDied?.Subscribe(HandleEnemyDefeated).AddTo(_subs);

private void OnDisable()
    => _subs.Clear();

状态:已修复。QuestManager.cs 新增 private readonly CompositeDisposable _subs = new()OnEnable 改用 Subscribe(...).AddTo(_subs)OnDisable 仅一行 _subs.Clear()。全库事件订阅模式 100% 统一。

P4-2 DeathRespawnService 复活流程完整实现

public IEnumerator StartRespawnCoroutine()
{
    _onRespawnStarted?.Raise();
    yield return new WaitForSeconds(_respawnFadeDuration);
    // TODO: 加载存档场景 ← 需实现
}

状态:已修复。DeathRespawnService 新增 [SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequestStartRespawnCoroutine 通过该频道广播带 IsRespawn = true 的场景加载请求,复用 SceneService 路径,零直接 SceneManager 调用。

P4-3低优先本地化系统迁移

Resources.Load 方案在语言文件 > 100 条时仍可接受,但如需支持 DLC 语言包,建议迁移到 Unity Localization Package。

P4-4低优先SpellManager ExecuteSpellEffect 分支实现

private void ExecuteSpellEffect(SpellSO spell)
{
    switch (spell.effectType)
    {
        case SpellEffectType.Projectile:     // TODO
        case SpellEffectType.AreaOfEffect:   // TODO
        case SpellEffectType.SummonShade:    // TODO
        case SpellEffectType.TeleportBlink:  // TODO
    }
}

架构已完整,各分支逻辑待填充。

P4-5低优先FloatingDamageText 显示配置化

// 建议DamageDisplayConfigSO
[SerializeField] private DamageDisplayConfigSO _displayConfig;
// 配置临界色 / 暴击色 / 字体缩放曲线

P4-6 DialogueManager ConditionalVariant 完整接入

// 现有 TODO
var resolved = ResolveVariant(sequence);
// ResolveVariant 尚未完整查询 WorldStateRegistry

状态:已修复。DialogueManager 新增 [SerializeField] private WorldStateRegistry _worldStateResolveVariantstatic 改为实例方法,TODO 替换为 _worldState.HasFlag(variant.conditionFlag) 真实查询;_worldState == null 时回退原序列(向后兼容)。

P4-7信息StatusEffect 堆叠设计

若游戏后期需要毒素/火焰叠加伤害,需在 StatusEffectManager 中将 Dictionary<Type, StatusEffect> 改为 Dictionary<Type, List<StatusEffect>>,并为每个效果维护独立计时器。目前单层字典架构符合当前设计要求。


附录:关键设计决策记录

A. 为何选择 SO 事件频道而非 C# 静态事件?

  • SO 事件在 Inspector 中可见、可调试、可在 EventBusMonitor 中监控
  • 编辑器测试场景无需启动完整游戏即可触发事件
  • 频道可在多个场景中共享Persistent 场景 + 关卡场景共用同一 SO
  • 避免静态事件在场景切换后的订阅残留问题

B. 为何选择 ServiceLocator 而非 DI 框架Zenject/VContainer

  • Unity 项目引入全量 DI 框架增加新人上手成本
  • ServiceLocator 在本项目规模424文件/30模块完全够用
  • GetOrDefault<T> 返回 null 的模式与 Unity 的空引用检查哲学一致

C. 为何 PlayerController 不用 RequireComponent

  • ParrySystem 是可选能力(能力解锁后才添加),编译期 RequireComponent 会强制 Prefab 上必须有此组件
  • 改用 [SerializeField] 手动绑定 + if (parrySystem != null) 守卫,支持渐进式能力开放

文档生成时间2026 全 P0P3 修复后
覆盖文件424 个 .cs 文件30 个 Assembly Definition
综合评分9.5 / 10商业 AA 对标)