状态同步与帧同步¶
先确定谁裁决¶
第一次做联机时,最先纠结的是“选状态同步还是帧同步”。后来发现这个问题问得太早。真正要先确定的是:谁来裁决结果,客户端能容忍多少延迟,以及是否需要回放。
状态同步和帧同步只是两套常见的组织方式,实际项目里也经常混着用。1
状态同步¶
服务器保存权威状态,定期向客户端发送快照或增量。客户端收到后更新画面,本地角色可以提前预测。
快照里通常不发送完整场景,而是只发玩家附近、当前可见、优先级高的实体。位置和旋转也会量化,减少包体。2
public readonly struct PlayerSnapshot
{
public readonly ushort PlayerId;
public readonly int Tick;
public readonly Vector3 Position;
public readonly ushort Yaw;
public readonly ushort Health;
}
位置量化得太粗会抖动,瞄准方向量化得太粗会有台阶感。量化误差要控制在玩法允许的范围内,不能只看带宽。
帧同步¶
帧同步一般只传输入,客户端按相同规则在本地推进模拟。只要初始状态、输入顺序、随机数和浮点结果一致,各端就会得到相同结果。
这种方式带宽低,适合单位数量多、输入频率稳定的 RTS 或格斗游戏。代价是确定性要求高,尤其是浮点运算、物理引擎和随机数。
常见做法是使用定点数,给随机数固定种子,并且每帧按固定顺序处理输入。即使如此,不同平台、不同编译选项也可能产生细微差异,所以需要状态哈希和断线重连方案。
怎么选¶
| 维度 | 状态同步 | 帧同步 |
|---|---|---|
| 权威方 | 服务器 | 输入共识或服务器转发 |
| 带宽 | 跟状态复杂度有关 | 通常较低 |
| 客户端预测 | 常用 | 本地直接模拟 |
| 回放 | 需要记录快照或输入 | 天然适合 |
| 反作弊 | 服务器可直接裁决 | 更依赖校验和监控 |
| 跨平台一致性 | 较容易 | 很难 |
如果是 FPS、MMO,或者需要服务器裁决大量战斗结果的实时对战,通常更适合状态同步。服务器可以直接处理伤害、命中和物品变化,客户端作弊空间相对小一些。
如果是 RTS、格斗、部分体育游戏,或者回放需求很强的项目,帧同步更容易控制带宽。但前提是团队能控制确定性。
客户端预测¶
状态同步如果完全等服务器回包,本地移动会有明显延迟。常见做法是保存未确认输入,先本地模拟,再在收到权威状态后重放:
如果预测和服务器结果差得不多,可以平滑修正;差得太多,直接拉到服务器位置更稳。这里不要在两种策略之间反复切换,否则玩家会看到角色来回抽搐。
多留一份输入记录¶
联机调试时,把每个 Tick 的输入和关键状态存成固定长度的环形 Buffer。出现不同步时,直接回放到出问题的 Tick,比在日志里猜快很多。
状态同步还可以额外记录服务器快照和客户端预测值。这样能区分是输入丢了、预测逻辑不一致,还是插值参数设置不对。