5.3 数据同步的分层设计:什么走复制、什么走 RPC、什么走外围服务

项目层面的数据流设计,比「某个属性怎么复制」更影响架构健康。四层模型供参考:

四层模型

层 内容 通道 频率
L0 实时核心状态 位置、朝向、移动模式 属性复制 + 客户端预测 30~60Hz
L1 游戏逻辑状态 血量、Buff、技能、装备、库存 属性复制 / FastArray 事件驱动
L2 一次性事件 受击表现、特效、提示 RPC(Client/Multicast) 事件驱动
L3 持久化元数据 等级、成就、好友、商城 游戏服务器 ↔ Web/DB,不进 UE 复制 登录/事件

各层的设计要点

L0:只放「必须每帧平滑变化」的数据。这一层的带宽是常驻成本,参数精打细算(旋转可以压缩、速度可由位置差分算)。

L1:事件驱动的状态。这里是最容易臃肿的一层——每加一个系统就想往复制属性里塞。纪律:

  • 每个 L1 属性问:「它变化时,所有相关客户端必须立刻知道吗?」不是 → 改成本地计算/聚合查询。
  • 用 FastArray 处理列表、用条件复制(OwnerOnly/SimulatedOnly)收窄广播面。

L2:纯表现事件。原则是事件幂等 + 不携带逻辑后果——就算这层全部丢失,游戏逻辑仍然正确。

L3:与 UE 复制完全解耦。玩家等级、商城购买这些走 HTTP/WebSocket 到你的后端服务,UE 网络只负责「对局内」。这条路的好处:

  • 对局服务器重启/换服不影响元数据
  • Web 服务可独立扩展(Web 团队并行开发)
  • 对局内复制的属性集保持精简

桥接接口:L3 到 L1/L2 的边界要清晰——玩家进入对局时,GameMode 从 Web 拉一次档案权威数据(L3→L1)写进 PlayerState;对局结束时,把结算数据推回 Web(L1→L3)。其他时候两层互不相干。

反面案例:全都搓进复制层

某项目把「玩家成就完成进度」「好友在线状态」「商城限时折扣」都做成了复制属性——结果:

  • 对局内带宽被无关状态占去 30%
  • 成就逻辑混在对局代码里,网页版改活动要对局热更
  • 每次加运营活动都要改网络代码

拆解后:L3 全走 Web,对局内只在入口/出口同步一次,网络层代码量减少 40%,运营改活动不动引擎。

规范模板:新同步需求的决策树

新数据/新功能
   ├─ 每帧变 + 影响表现? → L0(复制+预测/平滑)
   ├─ 事件变 + 多端共享? → L1(复制属性+OnRep)
   ├─ 一次性 + 纯表现?  → L2(RPC)
   └─ 与对局无关的元数据? → L3(不进复制,走 Web)

心得:网络层的代码是「一旦写进去就很难拆出来」的类型——因为同步机制渗进每个系统的细节。分层模型的意义在于「新功能先归层,再动手」,避免架构被无数个临时决定侵蚀。