前言

谈到网络游戏,不可避免要谈到现有两种比较常见的网游同步技术:帧同步和状态同步
说到这两个名词,大家夸夸奇谈,都能讲上些许自己的见解,我反正啥也不懂
这篇文章就打算着重学习一下这两种技术的基础和原理

网络同步的目标就是时刻保证多台机器的游戏表现完全一致。

网络同步 = 实时的多端数据同步+实时的多端表现

同步战斗逻辑是包括技能逻辑、普攻、属性、伤害、移动、AI、检测、碰撞等等的一系列内容,这常常也被视为游戏开发过程中最难的部分。

网络同步按大类来分有两种做法:状态同步和帧同步。需要强调的是这两个概念并不是简单的对立概念,其中的差异包括:”数据格式与内容”、“逻辑的计算位置”和“是否有权威服务器”等。

帧同步【LockStep】

LockStep的翻译是锁步同步,是齐步行军的意思

有明确的逻辑帧概念,按照固定的逻辑帧间隔同步数据

  • 帧同步的战斗逻辑在客户端
  • 在帧同步下,通信就比较简单了,服务端只转发操作,不做任何逻辑处理。
  • 客户端按照一定的帧速率(理解为逻辑帧,而不是客户端的渲染帧)去上传当前的操作指令,服务端将操作指令广播给所有客户端,
  • 当客户端收到指令后执行本地代码,如果输入的指令一致,计算的过程一致,那么计算的结果肯定是一致的,这样就能保证所有客户端的同步,这就是帧同步。
属性 帧同步(LockStep) 状态同步
确定性 严格确定 允许小误差,定时纠正误差数据
表现与响应速度 传统严格帧锁定要等其他客户端消息全部到达,响应比较慢;乐观帧锁定可以做到本地立刻响应,但是需要回滚的时候,体验就没那么好了 一般会做预测,可以做到立刻响应。不做预测的话,响应时间是一个往返时间(RTT)
带宽与流量 带宽随人数增加而增加,不适合MMO 需要发送各种状态数据,带宽占用比较高。可以通过压缩、裁剪、增量等方式优化。人数较少时候不如帧同步剩流量
网络延迟适应性 要求较低的延迟。如果延迟较高,所有玩家体验都不好。即使采用乐观帧锁定优化,高延迟下也容易产生卡顿 适应性较高,方便做各种插值优化。当然高延迟下,也容易产生位置突变
开发难度 初期开发减法,框架容易实现,但是后期解决bug和完善系统很困难。比如浮点数、随机数、执行顺序导致计算结果不一致,问题很难排查 框架比较复杂,客户端服务端一套代码,每个功能都需要客户端服务端联调。问题定位比较容易。也会出现时序问题
玩家数量 适合少量的玩家,比如ACT、MOBA 可多可少
跨平台 不适合跨平台,会有浮点数问题,可以用定点数来将误差控制在一个可接受范围,同时可以定时纠正结果 适合。有权威服务器
反外挂 P2P架构不适合反外挂,如果引入战斗服务器来校验各个客户端结果,可以解决常见外挂,但是透视和全图视野防不了 与服务器加入校验机制,可以起到比较好的反外挂效果。但是一样防不了透视外挂
中途加入和断线重连 比较复杂。可以在断线的时候,通过快捷播放服务器同步的帧数据来快速跟上游戏 容易。由于实时记录了各个对象的状态信息,所以重连的时候,直接创建这些对象,并同步信息即可
性能(客户端) 客户端要跑完整逻辑,还要执行渲染逻辑,开销比较大 可以灵活优化,客户端跑较少逻辑
回放(离线) 本身收集了所有玩家的输入信息进行逻辑推进,天然支持回放,且回放文件比较小 可以支持回放,但是逻辑比较复杂,需要不断记录状态信息,同时回放时候需要读取合适的时间。回放文件大
回放(实时) 比较复杂,客户端需要本地对全场状态进行序列化,才能回到目标时间。播完回放后还需要加速追上实时游戏状态 相对容易,可以方便的记录快照信息,并按照录制内容随时播放

状态同步

本节中提到的状态同步和帧同步都是广义上的,虽然它们的底层实现都可以基于上面提到的模式,但从广义上讲,它们的同步思路不同。

状态同步指的是将其他玩家的状态行为同步,例如,怪物的AI控制、角色技能释放、战斗伤害计算等。纯粹的状态同步模式下,这些内容都由服务器运算,只是将结果下发给客户端,客户端根据得到的数据驱动显示即可。这种模式的缺点是流量消耗比较大,消耗的流量取决于场景中需要转发数据的人数和内容。另外,先天的结构导致响应速度存在问题,无法做到高频交互的顺畅体验。最明显的特点就是“拉扯”现象。它表现为某个角色会突然出现在某个位置,或某个技能效果突然出现,甚至角色忽然死亡等。引发这个现象的原因是,网络波动导致数据未能及时送达,而客户端进行了某些程度的预表现或航位推测。

对于大多数实时性要求不高、交互简单的游戏,一般会使用状态同步。通常来说,常规MMO类型的游戏只能使用状态同步。

帧同步

帧同步是指客户端之间只同步用户的输入指令,例如,向前走、按下哪些技能等,不同的客户端各自计算自己的结果。由于消耗的流量只取决于指令数,因此会大大减少消息。另外由于消息结构的原因,帧同步的速度要比状态同步更快,因此适用于一些高频交互的游戏。

不过由于每个客户端需要独立计算,因此需要保证计算结果的一致性。理论上讲,相同的时机,输入相同的内容,会得到相同的结果。不过从实际情况看,做到完全相同还是有些难度的。以Unity引擎为例,一方面,各个脚本之间Start、Update等生命周期函数的调用顺序不确定;另一方面,使用Physic物理系统也不保证是确定性模拟。比如一个单位的坐标偏差了0.01导致技能未能击中目标,那么这个目标的血量判断就会受到影响。如果这个目标的行为受到血量的影响,那么最终结果会完全不同。因此让每个客户端计算结果完全相同不是一件容易的事。一些常见的注意事项如下。

  • 不使用浮点数,用整数代替。
  • 不同客户的同步频率要保证一致。
  • 随机种子相同,并自定义接口防止其他公用系统干扰。
  • 使用排序容器,保证遍历顺序。
  • 逻辑显示分离。
  • 使用补间过渡,调整速率,掩盖卡顿。

除了一致性的难点,帧同步还需要解决流畅度的问题。由于通过网络传输过来的数据一定会慢于本地,而我们又希望在相同的时刻输入信息,因此就会引发等待,反映到用户体验就是不流畅。

优化方法有很多,例如,在帧同步游戏中,由于广播的频率非常高,因此每次广播的数据就要足够小,这样可以节省很多消息处理的时间。对于消息,可以将需要所有客户端同时发生的内容提前广播给其他用户,采用时钟同步。客户端逻辑先行,显示通过平滑追赶的方式处理。很多改进是体验优化的范畴,需要结合具体游戏进行。

在传输层,移动端的同步建议使用UDP作为传输协议。TCP为了保证传输的可信性,很多机制不太适合波动较大的移动网络。在弱网络环境下,UDP的RTT几乎不受影响,而TCP的RTT波动比较大,特别是在丢包重发时影响比较明显。虽然使用UDP会引入丢包、乱序的问题,但可以通过冗余的方式来解决这个问题。比如每帧三个数据包,实际上是包含了过去两帧的数据,也就是每次发三帧的数据来对抗丢包。

帧同步难题

【游戏原理解析】帧同步核心技术解析-CSDN博客

网络方面传输难点

1.需要高频率同步的通讯
由于帧同步需要快速的响应,则需要高频率的网络通讯,由于Tcp冗余的确认机制,虽然能保证消息不会丢失,但也消耗的很多时间
基于此Upd更适合帧同步的模式,但Udp的缺点也很明显,就是有着不低的丢包率和每条消息到达时间不固定,对于帧同步每一帧不能丢失而且必须按顺序读取来说,这是个致命的问题

本地同步计算难点

2.不同平台的浮点数精度计算不同导致碰撞结果不同

由于不同平台对于float精度计算不同,精度截取总是不相同的,例如苹果和安卓,linux和windows等等
而Unity使用的碰撞使用浮点数计算的,所以帧同步不能套用Unity的碰撞器,得自己实现一个确定性的物理碰撞

3.确定性物理碰撞
上面提到,Unity的碰撞是基于浮点数实现的,所以不能用,则需要开发一套自己的物理碰撞检测
这个碰撞检测基于定点数实现确定性碰撞,后面会介绍
这里看样子很难,但是我们并非要实现像Unity里那样复杂的碰撞,只需要很简单的碰撞关系就行了,后面会介绍实现方法

4.表现层卡顿僵硬
由于帧同步是帧驱动,只有接受消息才会跑,(根据经验)每秒同步15帧是比较好的,这就导致了,每秒中数据会同步15次
当我们看玩游戏时,画面每秒动15次会像是在看ppt一样,而且由于每帧数据变动,中间状态过度会很僵硬,会给人很僵硬的感觉
这就需要把逻辑层和表现层分离,并在表现层做预测,后续介绍

5.同步操作如何驱动游戏逻辑
我们只知道同步了操作,但是操作如何驱动复杂的游戏逻辑呢
以及同步什么样的操作比较合适,这都需要一一解决,后面会介绍

解决方案

可靠快速的网络通讯 -kcp

浮点数精度问题 -定点数数学库

确定性碰撞检测 –AABB ○与矩形

帧驱动 –Tick 15ms :由于帧驱动,只有接受消息逻辑层才跑,不接受就锁帧(不跑),但每一帧到达的时间是不固定的,这就需要我们约定一个Tick时间,也就是约定每一个Tick间隔

逻辑层与表现层分离

预测及修正

乐观帧同步

帧同步分享_乐观帧同步-CSDN博客

网络游戏同步技术五:帧同步的实现与优化 | antsmallant

具体实现

​ 1.服务端每一秒,下发10到15帧的逻辑帧。不同项目也可以按照自己实际情况调整

​ 2.客户端对于每一个逻辑帧,进行补充3个渲染帧

​ 3.如果客户端一下收到很多连续的update,那就跨进播放

两种同步模式:状态同步和帧同步 - 知乎 (zhihu.com)

【网络同步】浅析帧同步和状态同步 - 知乎 (zhihu.com)

Unity多人游戏框架Netcode for GameObjects

Unity 实时多人游戏,第 8 部分:探索现成的网络解决方案 | HackerNoon

Unity 游戏框架之GameFramework和ET对比_unity et框架-CSDN博客

网络游戏知识——同步算法