1.3 网络相关性:不是所有人都要看见你的剑光
为什么需要相关性
想象一张 100 人同服的开放世界地图:如果每个玩家的每次 position 更新都广播给其余 99 人,带宽和客户端绘制压力都会爆炸。网络相关性(Network Relevancy)就是 UE 的流量阀门:服务器为每个客户端独立判断「这个 Actor 此刻对你看不见/无交互,就先不同步它」。
三档相关性设置
// Actor 头部或构造函数
bAlwaysRelevant = false; // 1. 永远相关:所有客户端始终同步(UI、规则管理器、全屏天气)
bOnlyRelevantToOwner = false; // 2. 只对拥有者相关:只有控制它的客户端能看到(如本地技能指示器)
第三档是默认的距离相关:NetCullDistanceSquared(蓝图里叫「网络剔除距离平方」)决定多远以外不再同步。
| 场景 | 建议设置 | 理由 |
|---|---|---|
| 游戏规则/比赛状态管理器 | bAlwaysRelevant=true | 无位置概念,所有人都要知道比分 |
| 玩家自己屏幕的准星/本地指示 | bOnlyRelevantToOwner=true | 别人根本看不见 |
| 普通怪物、可交互道具 | 距离相关,调 NetCullDistanceSquared | 省流量 |
| 远程玩家的角色 | 默认距离相关 | 配合 NetUpdateFrequency 调更新频率 |
距离相关的两个坑
坑一:相关是「进入/退出」事件驱动的。Actor 退出相关范围时,客户端的副本会被销毁(不是隐藏);重新进入时重新 spawn。这意味着客户端上该 Actor 的本地状态(如材质参数、客户端预测的插值缓存)会丢失。如果你的客户端逻辑依赖「Actor 持续存在」,要么加大剔除距离,要么把表现状态挪到可恢复的地方。
坑二:NetCullDistanceSquared 是平方值。想要 20000 单位(厘米)外剔除,要填 20000×20000=400000000,不是 20000。我见过有人填 20000,结果.Reload 不管多远都同步不上——其实是 141 单位(约 1.4 米)外就剔除了。
更新频率: relevancy 的「时间维度」
相关性管「同步不同步」,NetUpdateFrequency(网络更新频率)管「多久同步一次」:
- 玩家角色:默认 100Hz 太奢侈,通常调到 30~50Hz 就够。
- 远处怪物:5~10Hz 甚至更低。
- 完全静止的道具:设
MinNetUpdateFrequency为很低值,几乎不更新。
休眠(Dormancy) 是更激进的省流量手段:Actor 可以进入休眠状态,完全不发更新,直到被「唤醒」(如受击、被交互)。适合那种「满地图可破坏但平时没人动」的对象。官方文档:「Actor 网络休眠」(https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/actor-network-dormancy-in-unre-559d)。
相关性检查的自定义
默认是距离球体,你可以重写 IsRelevantForClient / IsNetRelevantFor 做自定义判定(例如「同一队伍无论多远都同步」)。注意这个函数在服务器每 tick 都可能对每个客户端调用,别写重逻辑。
判定流的小结
服务器每帧对每个客户端做三步筛选:
- Actor 是否在相关集合里(bAlwaysRelevant / bOnlyRelevantToOwner / 距离判定)
- 相关 → 按该 Actor 的 NetUpdateFrequency 决定本帧是否真的打包发送
- 休眠中的 Actor 直接跳过
心得:相关性配置属于「上线前必查项」。我们项目曾在发布后发现「BOSS 战时部分玩家看不到 BOSS 技能预警」——根因是预警 Actor 用了距离相关,而它的剔除中心是 BOSS 出生点不是当前位置,配合玩家的高速移动,预警频繁进出相关范围导致客户端反复销毁重建。后来给预警类 Actor 统一上了 bAlwaysRelevant 才解决。
