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 分钟就能理解"该往哪放";没有图,三个月后代码自己都说不清。
