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 都可能对每个客户端调用,别写重逻辑。

判定流的小结

服务器每帧对每个客户端做三步筛选:

  1. Actor 是否在相关集合里(bAlwaysRelevant / bOnlyRelevantToOwner / 距离判定)
  2. 相关 → 按该 Actor 的 NetUpdateFrequency 决定本帧是否真的打包发送
  3. 休眠中的 Actor 直接跳过

心得:相关性配置属于「上线前必查项」。我们项目曾在发布后发现「BOSS 战时部分玩家看不到 BOSS 技能预警」——根因是预警 Actor 用了距离相关,而它的剔除中心是 BOSS 出生点不是当前位置,配合玩家的高速移动,预警频繁进出相关范围导致客户端反复销毁重建。后来给预警类 Actor 统一上了 bAlwaysRelevant 才解决。