帧同步与状态同步:游戏同步机制的选择与比较

棋牌游戏开发 7个月前 126浏览 0评论

近年来,越来越多的游戏采用了锁步,关于帧同步和状态同步的讨论也很多。那么我们该选择哪种同步机制呢?两种机制都有使用过,各有优缺点,也踩过不少坑。这里就帧同步和状态同步做个总结和讨论。

首先需要说明的是,这里的帧同步其实是指LockStep,也就是服务端一帧一帧的转发客户端的操作,客户端进行确定性计算和一致性模拟(同步操作两边的客户端通过完全一致的操作计算出完全一致的状态),每帧的同步状态在这里也算是状态同步。

两种同步机制都是为了实现即时同步不同客户端的状态的目标。帧同步需要参与者管理和维护自己的副本,并通过应用一致的逻辑推动所有状态同步更新;状态同步则是不断比较和发送随时间推移最小的状态变化和差异。

我们首先来看一些使用这两种同步机制的例子:

可以看出,对于大多数类型的游戏来说,两种同步方式都可以使用,但绝大多数游戏都采用状态同步,大量玩家战斗的游戏只能采用状态同步。

网络模型发展历史

下面简单介绍一下网络同步模型的发展,我们可以参考DOOM和QUAKE(I/II/III)的演进看同步模型的发展,另可参考文章DOOM3网络模型与网络架构的演进。同步方式的历史大致有帧同步(Lockstep)、快照同步(Snapshotsynchronization)、状态同步(Statesynchronization),目前大部分多人游戏都采用状态同步。

P2P 模型(DOOM)

DOOM(1994)的网络模型是基于P2P帧同步的。帧同步存在各种问题,后面会介绍。而且因为没有主机,每个玩家都直接和所有其他玩家相连。如果任何一个玩家卡住了,所有人都会卡住。这是一项非常古老的同步技术。

数据包服务器(简单的数据包中继)

原版DOOM增加了一个Packet Server,负责转发所有的tick命令。玩家不再直接连接其他所有玩家,而是连接服务器(或者某个玩家的机器)来获取最新状态。经过这样的改进,同步量减少了,如果有玩家卡住了,就只有他自己卡住了,当然如果服务器卡住了,所有人都会卡住。同时,如果主机是服务器,可以避免大部分作弊行为,但如果主机是玩家,就没办法防止作弊了(可以参考魔兽世界,如果主机卡住了,所有人都会卡住,也没办法防止作弊)。帧同步的其他缺陷并没有得到解决。

客户端服务器 (Quake I/II/III)CS 架构

Quake I/II/III 实现了典型的 C/S 架构(1996 年)。在这个模型中,服务器负责所有的逻辑判断,客户端本质上只是一个渲染终端。玩家将自己的操作和输入发送到服务器,并接收一个需要渲染的实体列表。服务器以固定的频率向客户端发送压缩的快照。客户端使用这些快照来插入或推导流畅、连贯的体验。

此时同步机制已经变成了服务器同步操作客户端计算逻辑,服务器同步状态,解决了大部分帧同步问题。不过Quake I还是比较简单,不像帧同步,所有逻辑相关都放在服务器上,客户端发送操作后,必须等待服务器同步状态,延迟问题还是没解决。同时因为同步所有状态信息对带宽要求很高,越复杂的游戏对带宽的要求就越高。

Quake III 做了进一步的优化,客户端不再等待服务器,而是预测可能的游戏状态,预测状态和服务器端逻辑使用同一套代码,如果服务器和客户端确实不一致,服务器会进行准强同步。

预测也是降低延迟感的重要方式,对于对延迟要求较高的 FPS 来说尤其重要。另外,状态同步服务器始终掌握所有信息,允许玩家中途加入和退出。不过开发复杂度也变高了,代码需要区分服务器和客户端,逻辑和性能分离,处理一些联调问题(服务器和客户端处理时间不一致、预性能差异问题、强同步问题)。

基于Quake引擎开发的Half-Life在此基础上引入了延迟补偿,当玩家射击目标(几毫秒前的状态)时,真正做检测的服务器会用几毫秒前的目标状态来检查是否击中目标。这就需要服务器不断保存之前一小段时间的状态,不仅增加了实现的复杂度,还会导致一定的不一致性。延迟高的玩家更容易因为补偿而得到更有利的判定,严重影响游戏体验。这种补偿只能回滚目标的位置,但其他环境状态的改变都是无法逆转的,也影响实际体验。

在Quake III中,同步信息被进一步压缩和优化,只有PVS内的实体才会被同步,同步的是与上次同步的压缩差异(相对于上次快照的实体状态的delta压缩,Delta技术)。

可以看出,当玩家较少时,帧同步只需要同步操作,流量会比较小,非常适合同屏大量士兵的情况(士兵不需要同步任何信息),节省带宽。但是当玩家较多时,每个玩家的操作都要相互同步,带宽会成倍增加,无法优化。相反,状态同步可以通过划分区域的方式,同步支持更多玩家。

与 Quake III 不同,Doom III 的服务器和客户端使用相同的代码来更新/预测实体的状态,因此无需担心服务器和客户端逻辑互相干扰。同时,客户端和服务器也以相同的逻辑帧率 60fps 运行。客户端每帧上传玩家输入,服务器以固定间隔同步 PVS 范围内的状态快照,也可以理解为逐帧状态同步。

Doom III在网络上采用UDP,通过冗余包和滑动窗口保证服务端消息不丢失、有序,允许客户端上行丢包,在弱网情况下,延迟比TCP低,且不需要TimeOut机制。

两者优缺点比较

那么我们来比较一下帧同步和状态的优缺点:

如何选择

通过前面的分析,我们可以得出,对于绝大部分游戏来说,两种同步方式都可以使用,但相较而言,状态同步的适用范围更广,尤其适合复杂度高、延迟要求高、玩家多的游戏,比如FPS、MMO等;帧同步相对适合兵多、玩家少且固定、单局时间短、对打击公平性要求高、追求一致性的游戏,比如格斗、体育、RTS、卡牌、MOBA等。

从技术角度来说,帧同步有一些技术限制,比如大量玩家战斗、随时进入和退出、性能难以预测等,而状态同步则有更多的优化手段,可以更好地降低延迟感。可以说,使用帧同步的就一定能使用状态同步,但反之则不然。

当然帧同步也有自己的优势,实现成本比较简单,开发比较快(一套逻辑不需要联调),在玩家少兵多的时候(因为只同步事件不同步状态,所以通过网络传输的数据和游戏中物体数量无关),服务器性能和带宽开销极低,甚至可以没有服务器(服务器可以完全不跑任何战斗逻辑,只在需要反挂的时候跑),有点分散化,也很适合一些单机游戏转为联网游戏,很适合中小型公司(之前开发的一款MOBA游戏就只有一个服务器)。

在选择的时候,需要综合考虑游戏类型、未来需求、战斗时长、游戏模式、网络带宽、延迟响应、防作弊、开发成本周期、强度等因素来选择不同的同步方案,甚至混合使用。没有最好的技术,只有最适合的技术。以下是选择时可以思考的一些参考问题:

帧同步的困难

由于帧同步有一定的局限性,而且应用比较少,所以在最后补充了一些针对帧同步的优化方案和经验。

时延:帧同步很难预演,所以对网络和时延的要求更高,必须在网络层做更深更极致的优化(有些难以优化的状态同步游戏可以用预演蒙混过关)。

以下是之前尝试过的一些优化方法:

1.首先要分析延迟是否存在逻辑问题,比如我们最初出现一些延迟,就是因为逻辑是在FixedUpdate中执行的,更新顺序问题导致客户端操作慢了一帧,解决这个问题之后有明显的改善。

2、网络层极度优化,采用自研UDP,通过冗余包、滑动窗口保证可靠性、降低延迟,冗余包数量取决于MTU,并不是固定的。当然前提是每帧操作的包也要极度优化(定向操作切分、按位压缩等)。另外客户端上行可以允许丢包、不可靠,客户端关键操作即时发送(发送频率高于逻辑帧)网络游戏开发,网络多线程,多地多线部署搭配等。

3. 控制数据包传输频率。对于非关键操作,减少传输频率。服务器收到的操作会进行合并优化,进行帧聚合,避免堆积。甚至可以选择性丢弃部分数据包;

4、客户端可以进行一些预渲染。比较好的做法是将逻辑层和表现层分开,客户端表现层可以预渲染一定的位移,然后通过航位推算算法逐渐插值到逻辑层的位置。对于帧同步网络游戏开发,解决平滑位移的基础还是网络层的最终优化,然后再考虑平滑插值和预渲染。

性能:帧同步需要每个客户端完整计算包括士兵AI等所有逻辑,特别是很多帧同步游戏对帧率要求非常高而且稳定,不能卡顿,同时也需要更好的性能优化要求。

以下是之前尝试过的一些优化方法:

1.优化一些常用的客户端性能,保证客户端帧率稳定。例如多线程、帧共享、GC优化、预加载预编译、算法优化、使用更优的平滑算法来分担渲染层次差异和网络堵塞等。

2、一些在帧同步方面有优势的优化方法。将逻辑帧和渲染帧分离,降低逻辑帧。一般来说,MOBA游戏逻辑只需要15帧。当渲染帧高时,使用负载均衡来分割逻辑帧,消除卡顿等;

3.即使一些复杂的计算,也可以分布式或者基于服务器,一个人计算并将结果同步给其他玩家。

开发效率:帧同步实现简单但维护和查 Bug 困难,对工具、开发素养、规范要求非常高。尤其是帧同步一个小小的不一致就会导致双方不一致(一个操作、一个错误导致跳过某段代码、更新顺序等),而且往往不会在第一时间表现出来,积累一段时间后才会爆发。

以下是之前尝试过的一些优化方法:

1.避免在程序框架上,要求开发时0warning0error,细致的防护,逻辑层和表现层的更新帧率分开,一些随机数,顶点数学库等,底层库也要分开,有自动化的导表转换工具等,防止逻辑层用浮点数,不要固定随机等;

2.提供自动化检查工具。定期计算关键数据的哈希值,并检查每一帧。表现层使用逻辑层库时会自动报告错误。

3.错误排查工具。详细的本地日志,不同步时每个人上传到服务器的前100帧的日志;录音功能,记录每个玩家的同步操作信息,出现问题后可反复触发追踪。

一些小误会

帧同步会受到网络最差玩家的影响,延迟取决于最差的玩家。如果一个玩家卡住了,所有人都会卡住:这是早期 P2P 技术导致的,一个玩家必须等到所有玩家的操作完成后才能到达。Doom 一开始就有这个问题,如果使用 Packet Server(服务器或主机),可以避免这个问题,卡住的玩家只会影响自己,这就是乐观帧解决方案。

· 状态同步服务器需要运行逻辑:状态同步也可以使用客户端之间的状态同步服务器只进行转发,帧同步也可以使用服务器运行完整的逻辑(实时运行且结算后快速计算)。

帧同步或者状态同步只能选其一:也有项目同时使用帧同步和状态同步来满足不同需求,比如梦幻三国。线下比赛使用帧同步时,比赛中对玩家使用状态同步,解决全图挂机问题。

· 帧同步防作弊难度大:除了全图作弊(客户端信息完整)以外,其他作弊方式比较容易防作弊,王者荣耀的防作弊做得不错。

·MOBA适合帧同步:Dota2、LOL都是状态同步的。

点击关闭
  • 客服QQ:

    1234567

    -------------------

分享:

支付宝

微信