网络游戏同步方式,一般分为两种:帧同步与状态同步。而帧同步所属的网络同步方式,适用于高实时性、本地表现要求比较高的情况,多数用于房间类动作游戏。例如《魔兽争霸》、《王者荣耀》等。

# 简介

游戏同步方式主要分两类:

# 状态同步

顾名思义,同步游戏状态,如当前血量、位置,包括各种操作都是由服务器计算,然后直接下发执行结果。

主要特点有:

  1. 安全性较高。
  2. 属于不严谨同步,保证结果相同,但是不能保证同时间各客户端显示一致。
  3. 表现越复杂数据消耗越大 (例如大量子弹)。
  4. 对网络的要求不高 (相对),一般使用 TCP 协议。

# 帧同步

基本上由客户端计算,服务器主要起一个『转发』客户端操作的作用,表现准确度更高、效果更好,且能够像类似单击一般开发 (当然还是有差异的)。理论上在每个客户端上收到相同指令的表现与结果都应当一致。而且由于仅派发玩家指令,因此数据消耗相对比较小 (但是网络要求比较高)。

当然有好处,帧同步相对来说也有一些不足:

  • 网络要求比较高:因此一般采用 UDP 协议。
  • 反外挂能力比较弱:因为执行逻辑都在客户端本地。
  • 一致性错误:各个客户端处理结果产生差异 (这一点在程序调试时难度尤其大)。
  • 断线重连时间长:相对状态同步的断线重连只需要再完整请求一次数据而言,帧同步断线之后,必须通过『追帧』(也可以理解为加速播放) 或重头再根据指令播放一遍抵达当前『帧』才行,这就导致更费时间。
  • 一般只适用于人数较少的『房间类』游戏:因为接受到其他客户端的操作指令后,本地也必须真的执行计算才能得到结果,若人数过多,不说一致性问题,执行压力也会很大。

总之,在看了 N 篇网络上各种相关文章之后,帧同步的实现大概可以总结为以下几点:

  1. 显示层与逻辑层分离 -> 保证逻辑帧为主
  2. 随机数确定 -> 多数语言内置随机数为线性同余发生器产生,指定了随机种子后,随机序列即固定 (或自己实现)
  3. 浮点数误差 -> 作定点数处理
  4. 玩家操作同步

做到上述几点之后,便可以基本保证各个客户点的一致性表现,若同时记录下玩家操作指令,后续甚至可以简单实现战斗过程的回放 —— 因此,亦可以实现服务器重算战斗,验算战斗结果等。

# 分类

# 帧锁定同步

也叫严格的帧同步、也有人称为囚徒模式,一般为老一代的 P2P 局域网帧同步游戏所用,服务器每帧收集所有客户端输入指令,再统一下发。

若某个客户端出现网络延迟,没能及时上传操作,服务器也会一直等待,导致所有客户端都出现『等待』。即『网速慢的玩家会卡到网速快的玩家』。可以说所有客户端的延迟都将等于延迟最高的客户端。

这种方式实现的帧同步,由于每个客户端指令都是严格一致,一般来说同步性便不成问题。
就如同两个手柄插在一台主机上,两个人在一块玩游戏一样。

# 乐观帧锁定同步

网络慢的卡了,导致所有玩家都没法操作,对现在的网络游戏来说那肯定不行。

于是有了这种方式,做法就是不再锁帧,客户端及时上传操作指令,且不管某个客户端有没有上传,服务器都会定时转发接收到的操作给每个客户端。

网络慢的玩家没及时收到操作,或者没及时上传操作,影响的都只是自己本地。

# 简单流程

  1. 战斗服发送初始数据,通知客户端准备
  2. 各客户端准备:加载场景 -> 初始化角色 -> 设置初始状态 -> 向服务器发送准备完成
  3. 服务端发送开始更新消息
  4. 进入更新循环:客户端发送指令 <-> 服务器分发指令
  5. 等待结束,若需要后台验算,则结算结果等待服务器验证

简单流程图

上述流程目前为看了些文章之后的部分个人猜想,乐观帧锁定同步应该会更为复杂,应当还有一些额外操作或优化需要处理。不过由于目前暂未上手试验,因此也并不完全确定,实际情况得等后面实现之后才能验证了。

# 协议

如上述所言,由于帧同步网络要求比较高,因此一般使用 UDP,或者目前还有开源的 KCP,在 UDP 基础上实现的可靠 UDP 协议。

当然,如今随着网速的加快,TCP 也不是不行。听说现在有的项目就是直接使用 TCP 开发,若是无法满足要求,再考虑切换至 UDP。

# 结束

通常,即使是帧同步游戏,也必然存在状态同步。

例如一般的匹配游戏,在匹配进入战斗之前,各种数据等等一般均走的 TCP 协议,且为状态同步流程。

帧同步则用于进入『房间』之后的每场玩法逻辑的同步。


参考文章:

  • 再谈网游同步技术
  • 王者荣耀技术总监复盘回炉历程
  • 帧同步游戏开发基础指南
  • 知乎
  • 修正歧义:帧锁定同步 (frame lock sync)& 按帧同步(frame sync)& 状态同步 (state sync)