4.2 CharacterMovement 的客户端预测与回滚

UE 的现成答案

CharacterMovementComponent(CMC)内置了完整的客户端预测机制,是 UE 网络设计中最精巧的部分。核心循环:

客户端:                              服务器:
 1. 采样输入 + 本地移动 tick      →   收到输入包,按序 Tick 移动
 2. 记录「输入 + 时间戳」快照           广播当前位置(复制属性)
 3. 本地立即应用移动结果                ↑
 4. 收到服务器位置 → 对账      ←──────┘
    若偏差 > 阈值 → 回滚到服务器位置(校正瞬移)
    若无偏差 → 继续本地 tick

关键点:客户端不是「移动前问服务器」,而是「本地先走,服务器事后对账」。99% 的时间客户端预测是对的,玩家完全感受不到延迟;只有预测出错(被别的玩家挡住、被技能影响)时才发生校正。

两个核心概念:SavedMoves 与 ServerMoves

  • FSavedMove_Character:客户端把每帧的输入(移动方向、按键状态、时间戳)打包存档。这就是「我做了什么」的证据链。
  • ServerMove RPC:客户端按序把这些输入发给服务器。顺序保证——服务器按客户端的顺序依次回放,保证两端执行同一串输入 → 浮点误差累积可控。

服务器回放时不是重新模拟全部历史,而是递增式——它保留自己上一次确认的状态,从那之后按收到的输入继续 tick。

校正(Correction)如何发生

客户端收到服务器的位置(通过复制属性 ReplicatedMovement + CMC 内部的同步)后,与本地预测位置比较:

  • 差距极小:忽略,继续预测。
  • 差距超阈值:客户端执行回滚(rollback)——把角色瞬移到服务器位置,然后用本地已存的输入包从那一时刻重新模拟到当前。玩家看到的是「一个快速的瞬间修正」,而不是「持续漂移」。

这套机制让移动手感几乎不受延迟影响,同时保证服务器权威(任何作弊客户端顶多让自己的预测错乱,影响不了服务器上别人的世界)。

常见问题:校正瞬移

如果校正频繁,玩家会感到「一卡一卡的瞬移」。根因排查顺序:

  1. 客户端 tick 与输入采样率不一致:CMC 的预测基于固定时间步,输入采样频率不稳定导致误差累积。
  2. 服务器物理与客户端不一致:两端碰撞/物理模拟有差异(例如客户端开了「低质量物理」),预测必然错。检查两端的物理设置一致。
  3. 移动组件被外部代码强行 SetActorLocation:绕过 CMC 的移动破坏了预测对账。
  4. MaxPositionError 阈值过小:投影误差频繁超过阈值。适当放大可减少校正频率(代价是容忍误差)。

相关设置项

[/Script/Engine.GameNetworkManager]
; 校正误差阈值(单位:厘米)
MaxClientPositionError=1.0
MaxServerPositionErrorError=0.5
; 客户端移动相关
[/Script/Engine.CharacterMovementComponent]
NetworkSimulatedSmoothLocationTime=0.1     ; 模拟代理平滑时长
NetworkMaxSmoothUpdateDistance=256.0
NetworkNoSmoothUpdateDistance=512.0        ; 超此距离直接瞬移不平滑

不要与预测系统打架

新手最常犯的错:自己写移动逻辑(AddMovementInput 之外又直接改 Location),或者用物理 Body 推动角色——这些都绕过 CMC 的预测链,导致客户端与服务器分叉。联网项目的角色移动,能交给 CMC 就全交给 CMC。

心得:CMC 预测机制值得每个联网程序员通读一遍源码。它把「客户端预测」这个理论概念工程化到极精细的程度(输入快照、序回放、误差对账、回滚重放)。自己实现预测系统前,先看看 UE 怎么做的。