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)——把角色瞬移到服务器位置,然后用本地已存的输入包从那一时刻重新模拟到当前。玩家看到的是「一个快速的瞬间修正」,而不是「持续漂移」。
这套机制让移动手感几乎不受延迟影响,同时保证服务器权威(任何作弊客户端顶多让自己的预测错乱,影响不了服务器上别人的世界)。
常见问题:校正瞬移
如果校正频繁,玩家会感到「一卡一卡的瞬移」。根因排查顺序:
- 客户端 tick 与输入采样率不一致:CMC 的预测基于固定时间步,输入采样频率不稳定导致误差累积。
- 服务器物理与客户端不一致:两端碰撞/物理模拟有差异(例如客户端开了「低质量物理」),预测必然错。检查两端的物理设置一致。
- 移动组件被外部代码强行 SetActorLocation:绕过 CMC 的移动破坏了预测对账。
- 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 怎么做的。
