在 UE 联网开发中,理解每一个组件的作用范围是避免 Bug 的重要方法。我们把它们按 可见性 和 生命周期 整理如下:
1. 核心组件分布图(谁能看到谁)
| 组件 | 服务器 | 拥有者客户端 | 其他客户端 | 核心职责 |
|---|---|---|---|---|
| GameInstance | ✅ 有 | ✅ 有 | ✅ 有 | 跨关卡数据存储(不联网)。 |
| GameMode | ✅ 有 | ❌ 无 | ❌ 无 | 游戏规则、胜负判定、玩家出生。 |
| GameState | ✅ 有 | ✅ 有 | ✅ 有 | 单局游戏全局状态(得分、进度)。 |
| PlayerController | ✅ 有 | ✅ 有 | ❌ 无 | 玩家私人专线(输入、私人 UI)。 |
| PlayerState | ✅ 有 | ✅ 有 | ✅ 有 | 玩家公共数据(名字、装备、血量)。 |
2. 易混点 OR 踩坑
① GameMode (游戏模式)
-
只存在于服务器,客户端根本不知道 GameMode 的存在。
-
核心任务:
- 决定谁能加入游戏。
- 控制玩家在哪个点出生。
- 判定游戏结束条件。
-
注意:别在 GameMode 里写客户端逻辑,因为客户端获取不到它,
Get GameMode在客户端永远返回Null。
② GameInstance (游戏实例)
-
生命周期最长,从你打开游戏进程到关闭游戏,它一直都在。
-
非联网组件:它不参与网络复制, 服务器有一个,每个客户端也各有一个。
-
核心任务:
- 存放关卡切换时不能丢的数据(如设置参数、未保存的角色信息)。
- 处理网络报错提示。
-
注意:它是本地的。如果你在客户端 A 的 GameInstance 改了变量,服务器和其他客户端完全看不见。
3. 网络执行生命周期(玩家进入流程)
当一个玩家加入正在运行的服务器时:
- 服务器 GameMode 收到请求,检查是否允许加入。
- 服务器 为其生成 PlayerController 和 PlayerState。
- 服务器 触发它们的
BeginPlay(服务器侧)。 - 客户端 加载地图,生成本地镜像。
- 客户端 触发它们的
BeginPlay(客户端侧)。 - 同步开始:服务器将 GameState 和 PlayerState 的最新数据快照同步给新玩家。
4. 变量复制模式
① Replicated (普通复制)
-
底层原理:服务器变量值改变后,引擎会自动将新值序列化并发送给所有客户端。客户端收到后直接覆盖本地变量。
-
适用场景:
- 非瞬时表现的数据:比如玩家的等级、称号、当前持有的金币数。
- UI 轮询(Polling)逻辑:如果你的 UI 是每隔 0.1 秒主动去读一次变量值,用这个就够了。
-
缺点:客户端不知道变量“什么时候”变了,只能被动接受覆盖。
② RepNotify (带回调的复制)
-
底层原理:除了自动更新变量值,引擎还会自动生成一个名为
OnRep_变量名的函数。当客户端收到服务器发来的数据更新时,会自动触发这个函数。 -
适用场景:
- 驱动视觉反馈:比如“血量”变了,不仅要更新数值,还要触发红屏闪烁或播放扣血特效。
- 刷新 UI 列表:比如你的背包数组变了,触发
OnRep里的事件分发器,命令 UI 重新绘制列表。
-
注意:
- 服务器默认不触发:服务器直接修改变量时,默认不会跑
OnRep函数(除非你是服务器玩家且逻辑特殊处理)。通常需要手动在服务器逻辑后Call一次该函数或分发器。 - 执行顺序:引擎保证在进入
OnRep函数之前,本地变量已经变成了服务器同步过来的最新值。
- 服务器默认不触发:服务器直接修改变量时,默认不会跑
5. 联机注意项
① BeginPlay 的执行时机
每当新玩家加入,服务器都会为该玩家生成新的 Controller 并跑一遍 BeginPlay。
- 注意:如果服务器本身也是玩家(主机玩家),它也会执行新玩家的
BeginPlay。 - 对策:初始化 UI 必须加
Is Locally Controlled过滤,否则主机屏幕上会叠加弹起别人的 UI。
② 三类自定义事件 (RPC)
决定逻辑在谁的电脑上运行:
- Run on Server:客户端 ➔ 服务器。用于申请改钱、扣血等权限操作。
- Run on Owning Client:服务器 ➔ 拥有者客户端。用于发私信、震动本地手柄。
- Multicast:服务器 ➔ 所有客户端。用于播特效、放声音、全服通告。
③ Multicast 为什么必须在 Server 执行?
- 物理限制:客户端之间没有通路,A 无法直接命令 B 的屏幕。
- 权限安全:服务器是唯一裁判,防止客户端非法广播。必须先
Run on Server申请,再由服务器发起广播。
6. UI 应该如何初始化?
最稳健的写法是:
- UI 的壳子:放在 PlayerController 里。判断
Is Locally Controlled确保只有本地创建。 - UI 的数据:放在 PlayerState 里。勾选
RepNotify。 - UI 的刷新:变量更新时可用事件分发器,通知执行 UI 更新。
- 全局信息(如比赛时间) :从 GameState 获取。
7. 总结
- GameInstance:本地记忆,跨关卡不丢,但不联网。
- GameMode:上帝规则,只在服务器,客户端拿不到。
- GameState:公共账本,全员可见,存大局进度。
- PlayerController:私人遥控器,处理本地输入和 UI,初始化必加本地判断。
- PlayerState:公共名片,全员可见,存玩家属性和装备。
- Replicated:静默同步,适用于后台数据。
- RepNotify:响应式同步,适用于需要即时触发动画、音效或 UI 刷新的关键变量。
- 同步准则:向上申请(Server RPC),向下同步(OnRep / Multicast)。