1.2 系统依赖关系图:先定方向,再写代码

核心结论

多系统项目最常见的死法是循环依赖:战斗依赖 UI、UI 依赖存档、存档依赖战斗。写第一行代码前,先画一张有向无环的依赖图,规定"谁可以调用谁"。这张图是后面所有架构决策的依据。

六大系统分层

层 系统 可依赖
L0 核心框架(GameMode/PlayerState/存档接口) 无
L1 输入(Enhanced Input) L0
L1 GAS 战斗 L0
L1 角色移动/动画 L0
L2 AI(行为树/感知) L1(只读接口)
L2 世界(刷怪/掉落/任务) L1 接口 + L0
L3 UI/HUD L0~L2 的事件
L3 特效/音效(反馈) L1 事件 + L2 事件
L4 联机复制 L0~L1(权威在 L1)

核心纪律: 1. 只允许上层依赖下层(L 编号大的可以引用小的,反之禁止)。 2. 层内系统互相通信走事件/接口,不直接引用具体类。 3. 跨层的强耦合(如 UI 直接读战斗内部状态)必须在 review 时拆掉。

具体依赖图(文字版)

                 ┌──────────────┐
                 │  L0 核心框架  │
                 └──────┬───────┘
        ┌───────────────┼───────────────┐
        v               v               v
   [输入 L1]       [GAS 战斗 L1]   [移动/动画 L1]
        │               │               │
        └───────┬───────┴───────┬───────┘
                v               v
          [AI L2]          [世界 L2]
                │               │
                └───────┬───────┘
                        v
              [UI/HUD L3]  [特效/音效 L3]
                        │
                        v
                 [联机复制 L4]

关键接口设计原则

1. 战斗对外只暴露事件

UI、特效、音效不订阅战斗内部状态,而是订阅战斗抛出的事件:

  • OnDamageDealt(AActor* Target, float Damage) —— 给飘字 UI。
  • OnCharacterDied(AActor* Victim) —— 给世界(刷怪管理)与 UI。
  • OnSkillCast(UAbility* Ability) —— 给特效与冷却 UI。

规则:战斗系统里这些事件的生命周期独立于监听者——即使 UI 没加载,事件也安全触发(记录在 PlayerState/GameState 上而不是 Widget 上)。

2. 存档分层:L0 定义接口,各层实现自己的数据

SaveGame 对象结构
├─ 总局:关卡进度、游戏时长(L0)
├─ 战斗:解锁技能、属性(L1 -- 由 GAS 序列化)
├─ 世界:已探索区域、掉落种子(L2)
└─ 玩家偏好:画质/按键设置(L3,独立文件)

各层向 SaveGame 注册自己的数据块,由 L0 统一触发 Save/Load。没有"存档神类"——那是铁定会失控的。

3. 联机放在最外层(L4)

复制规则写在各系统的数据定义层(GAS 属性已自带复制),但会话管理(创建/加入/断线重连) 独立成 L4 的一个模块,只依赖 L0 接口。这样单机开发和联机开发可以切换而不动核心。

常见坑与解法

坑 1:UI 里直接 Cast 到战斗组件读血量

后果:UI 改一下要重编译战斗;联机时 UI 在客户端 Cast 服务器数据直接懵。

解法:用接口/事件订阅。参考本系列蓝图与 C++ 那本书的"通信方式选型"。

坑 2:存档类什么都塞

后果:任何系统改动都可能弄坏存档。

解法:分层分块 + 每个数据块带版本号(升级时迁移)。

坑 3:新加系统时不知道放哪层

判断题: - 它被谁需要?(需要的越多,层越低) - 它需要谁?(需要的越多,层越高) - 答案冲突 → 它其实是两个系统,拆开。

相关节点速查

  • 通信方式:蓝图接口/事件分发器(https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/blueprints-visual-scripting-us-a68a 通信章节)
  • GAS 事件:AbilitySystemComponent->OnDamageDealt 之类(GAS 指南 https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/gameplay-ability-system-for-un-2c2c)
  • 联机分层:见本系列网络复制那本书
  • 输入层:Enhanced Input(https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/enhanced-input-in-unreal-engin-c7d0)

心得:这张依赖图我画了一下午,贴在显示器旁边。每次写代码前抬头看一眼——"这一行在向哪一层伸手"。架构不是设计出来的,是被纪律守住的。有一张图,团队新人 10 分钟就能理解"该往哪放";没有图,三个月后代码自己都说不清。