游戏中如何实现帧同步有几种实现方法

网络游戏的移动同步(五)帧同步算法-GAD腾讯游戏开发者平台后使用快捷导航没有帐号?
 论坛入口:
  |   |    |   | 
关于帧同步和网游游戏开发的一些心得
文/kisence
从今年下半年开始制作一款实时对战游戏以来,我就在着手写一个帧同步的游戏框架,其中包含了服务器框架和客户端框架,该框架目前已经开源。
期间踩过无数的坑,充分领略到了国内中文技术文档十分稀少和零散的问题,所以在这里我想写下我走过的路,以便于后来者参考。
首先,我希望写一个前后端能统一语言的框架,以至于在前端写好的游戏逻辑,拿到后端就可以直接使用。
其次,我的目标是写一个同步框架,在框架层面解决同步问题,在此之上写游戏逻辑的时候不需要再考虑游戏同步的问题。
目前看来,这两个目标都得到了比较好的完成。
下面是解决方法。
首先要解决的是前后端语言一致的问题
这里我使用了一个c#服务器框架 SupperScoket
遇到的几个坑是:
1.导出这个框架到在Mono上运行时报一个找不到window API的错误,解决方法是使用.Net 4.0以上版本的SupperSocket
2.框架在使用TCP模式时,有时会报出一个Send byte Time out 的异常,解决方法是使用TrySend方法,并在返回false时关闭连接。
第二个要解决的是同步框架的问题
这个问题比较复杂,如何在书写游戏逻辑的时候感受不到同步问题的存在?如果每个事件都要等服务器的回包,还要体验流畅,只能从预处理和追赶两个角度入手。
预处理就是,这个事件还没有发生,但是考虑到网络延迟的存在,提前先把结果发送给每个客户端,然后客户端到了这个时刻再把这个事件表现出来,典型的例子就是皇室战争。
如果说没有办法做到预处理呢,比如说玩家的操作需要立即响应,那么其他玩家收到这个事件的时候必然已经迟了,所以就要做追赶,比较典型的就是影子跟随算法。
但是这两种做法必然要在游戏逻辑中做对应的处理,开发者要时刻清醒此时是预测还是追赶,增加逻辑的复杂性,而且游戏的表现可能也参差不齐,有些地方也许同步的好,有些地方可能不好,要调优需要在每个地方都下功夫,增加开发时间。
那么应该怎么办呢,最理想的方法当然是全部当成本地计算,这样就无需考虑是追赶还是预测的问题了,那么网络游戏怎么全当成本地计算呢?当然就是帧同步了。
关于帧同步网上已经有很多资料,在此不再赘述,但是关于帧同步有一个核心的问题,那就是它在网络差的时候表现很差,这一点我们可以从星际争霸和魔兽争霸这些游戏中看出来,一旦有人卡顿,所有人都要停下等这个人的消息,但是我们知道手游《王者荣耀》这款游戏就是帧同步做的,他是怎么解决这一问题的呢?在《王者荣耀》负责人在unite 2017大会分享中我们没有看到这一解决方案,这可能是他们的商业机密,但是在看了暴雪分享的守望先锋同步机制之后,我得到了一个我自己的解决方案。
那就是预测回滚和解。
原理很简单,游戏开始时,每个客户端按照帧同步的方案推进着游戏,但是如果遇到服务器没能及时返回其他玩家操作的时候,给对应的玩家预测一个操作(复制该玩家最后一次操作),并继续推进游戏,如果在其后收到了服务器玩家关于这个人的操作,则把游戏回滚到预测开始的那一帧重新计算一遍,然后和现在游戏世界的表现和解。
如果服务器迟迟没有收到某个玩家的消息,则会给这个玩家预测一个消息(复制该玩家的最后一次操作)然后推送给所有玩家,包括那个掉线的玩家。其他玩家会以这个预测操作为准计算接下来的游戏世界,而这个掉线玩家也会收到这个预测操作,并且替换掉玩家实际进行的操作,重新计算一遍游戏世界。保证每个客户端的输入一致。
原理说起来简单,但是其实有几个难点。
第一个难点就在于回滚,如何回滚到预测开始的那一帧呢,要记录下每一帧的变化,然后逐帧退回吗?还是把每一帧的数据做一个快照保存下来?
其实这个问题实现起来不难,关键是从性能考虑,如果把每一帧的数据都快照下来,内存可能会吃紧,如果做逐帧退回的方式,实现起来相对复杂,并且在性能上也可能有问题。
这里就引入了ECS架构帮助我简化了这一问题,在ECS架构中,C 也就是component(组件),它是纯数据的集合,并且 E 也就是 Entity(实体) 集中存放在一起,这方便了我对它们的集中操作,
在ECS架构的帮助下,我实现了对组件进行快照式的存储,对实体进行了增量式的存储,实现了对数据的回滚。
第二个难点在于和解,由于预测操作和玩家真实操作的不同,重计算出来的世界必然预测的世界有差异,那么怎样以尽量不引人注意的形式,把预测世界过渡到真实世界呢,这一点守望先锋的分享中提到了一部分,但是没有完全解答这个问题。
实际上解决这个问题的思路是,先确定哪些是可以和解的,哪些是不可以和解的,然后分头处理。怎么分头处理呢,就是可以和解的在预测计算中就表现,不可以和解的,要等到真正的数据来了才进行表现。
那么哪些是可以和解的呢?就是在玩家不知不觉间就可以过渡到的,比如说物体的位置,动画。这里有很多的技术可以做这种和解,比如说影子跟随算法。
不可以和解的比如说冒出的血条数字,你不能说伤害数字都冒出来了,然后又塞回去。
但其实有一个难点是,飞弹能不能和解?
显然,飞弹的位置是可以和解的,但是飞弹的创建与销毁呢?这里涉及到一个游戏表现的问题,如果飞弹的创建要等到服务器回包才出现,那么这个表现在网络差的时候就太糟糕了。
所以一定要可以和解,不能和解创造条件也要和解。
下面是解决方案
其实一部分解决方法在难点1已经提到了,首先要建立一个对实体的回滚系统,保证飞弹能回滚。
但是还有一个问题,在回滚的过程中要先把这个飞弹销毁,但是如果重计算的结果是仍然创建这个飞弹呢?难道要再把这个飞弹再创建一次?虽然我们可以用池来避免频繁的创建销毁,但是粒子系统从池中取出仍然有重新构建的开销。
很自然的想到可以延迟派发创建的事件,在数据层面这个实体已经被重计算的很多次了,但只要这个实体仍然存在我就不再派发这个实体的创建事件。销毁也是一样。
但是我如何确定我两次创建的实体的是一个呢?要知道我们框架的设计目标是开发时尽量避免对同步系统的感知,也就是我们游戏逻辑并不知道现在的数据是真实的数据还是预测的数据,要在创建这个体的的时候判断这个实体是否已经在预测时创建过了显然不应该是我们游戏逻辑应该做的,可我们的框架又如何确定两个实体是否一致呢。
直接比较它们两个是否相等肯定不行,把他们的数据取出来一一比对又太耗时。
我采用的方法是我称之为特征码的方法,在构建一个实体的时候,用一个字符串去描述这个实体,这个字符串要尽量简略而又不能与其他特征码重复,然后自己实现的hash方法(.NET自带的GetHashCode有平台差异)把这个字符串转化为一个Int作为这个实体的唯一标识符,在创建实体的时候,只需要判断这个实体ID和缓存中的ID是否一致就可以判断这个实体是否已经在预测中存在了,从而实现延迟派发。
再说一点其他的技术细节
1.实体的集中创建与销毁
现在的架构中可以看到是一帧结束后把所有的实体集中的创建与销毁,为什么要这样做呢,实际上是为了重计算服务,当重计算进行时要先进行回滚,我根据回滚数据得知它是在某一帧里被创建的,但是不知道在哪个系统中,这就有可能造成,在实际计算中某个对象实际上在稍晚的时间被创建,不会被较早时间执行的系统所影响,但是回滚后,这个对象被创建在了较早的时刻(这一帧的开始),导致较早执行的系统也影响了他导致计算错误,为避免这一问题,我采用统一创建和销毁时刻的方式解决。
这一方式有一个问题,就是创建飞弹等对象时至少要延迟一帧,在主观感觉上就慢一点,守望先锋也提到了这一问题,他们提到后来把创建的时刻重新拿回了游戏逻辑内,我估计是在保存回滚数据时把是在哪个系统创建的实体也保存了下来,这样就可以避免计算错误的问题,目前在我的框架里还没有优化这一问题。
2.断线重连
关于断线重连这一点,使用ECS架构的优势就体现出来了,传统的帧同步断线重连只能把所有的玩家的数据从头输入一遍重计算,时间很长,而ECS可以很方便的把服务器的当前数据全部发送给重连的客户端,让客户端直接从重连的那一帧开始游戏,避免了漫长的重连过程。
3.常见的不同步情况
1.MomentComponentBase 组件的DeepCopy方法没有正确的拷贝全部数据
2.有些组件从本地创建和通过服务器同步消息创建的值有差异(比如有些组件有特殊的构造方法,而通过服务器同步的组件不执行构造方法)
3.前后端代码不统一
4.前后端数据表不统一
5.在进行整数计算的时候,数值溢出
6.浮点数计算误差(读表也会出这个错误)
7.同步逻辑之外的数值修改(例如付费复活)
关注我们官方微信公众号
下载我们官方APP-游戏行
关注手游动态微信公众号
作为策划中的一枚.消灭0回复, 最近也在看一些帧同步的知识。 学习了.
游戏版号7月将下放一批 同时游戏公司资质审吉比特、飞鱼继续投资TapTap 估值增长至近2斗鱼直播一姐冯提莫继卢本伟后再次被“央视你还能在游戏行业混多久?MMORPG《楚留香》手游架构首次深度分析手游赚了3亿美元奶粉钱后,他们又做了一款
微信扫一扫关注我们→帧同步游戏开发要点 - 简书
帧同步游戏开发要点
先说一下我们在研究和使用了帧同步之后,得出的结论:
如果项目没有录像、观战功能,请先放弃使用帧同步的念头,尝试使用状态同步。因为设计得好的状态同步,可以在很少流量基础上,完成类帧同步的效果。除非在通信上没有压缩空间,再考虑帧同步。
如果项目需求有中途加入,且不能容忍从头Replay带来的等待,游戏互动玩法(游戏内个体之间的相互作用)还非常复杂,则请慎重考虑是否使用帧同步。
确定性运算
浮点:目前除了主机平台上由于其平台的统一性使用的技术不同外,其他平台基本上都是使用的定点数做的确定性运算。这需要将所有涉及同步部分的逻辑运算,都要使用定点运算。这个工作,对于不同规模的项目而言,工作量可大可小。如果项目中引用了第三方库到同步逻辑部分的话,那么这个第三方库也要被定点数重写。关于定点数,如果没有太多的精力自写的话,可以尝试在网上查找定点库,网上已经有一些开源的定点库,可以拿来直接用,但注意在使用这些库时的精度问题。因为这些定点数库的设计,在使用时,依然使用浮点数作为定点数的声明和定义,面对不同的编译器和运算器,浮点转成定点数的精度处理上,可能是不一致的。所以,在使用前,请验证精度有没有问题。或者采用其他的方式,比如不再使用浮点数进行声明和定义。
随机:如果游戏中同步部分的逻辑,使用了随机,则需要自己实现一套跨平台的随机算法,保证所有平台随机的一致。当然,随机还会带来另一个问题,就是中途加入时随机数的一致性问题,要保证中途加入的客户端,执行与其他客户端一致的随机序列。这些可能需要我们在实现随机算法时,兼顾到需要同步到另一端的需求。
物理:基本上,大部分的游戏都会用到物理,抛开物理,也会用到碰撞。因为确定性运算的问题,我们需要重写一套确定性物理引擎。
动画:如果游戏部分同步逻辑,比如AnimationEvent,坐标等是由动画驱动的,那这部分也需要重新设计,不能使用内置的驱动逻辑。
其他:这里就包括所有涉及浮点的非运算逻辑了,比如invoke、yield等,这些接口要避免使用。
时序:要保证不同的客户端,数据的存储、逻辑的执行保持时序一致。所以,一些常用的数据结构就不能胜任了,比如常用的Dictionary、hashset等,需要我们使用其他的数据结构,比如类SortedDictionary这个效率偏差,或者自写一套保证时序的数据结构和算法。
难点-中途加入
帧同步的难点在于中途加入,当然这里的中途加入,不讨论从头Replay一遍的方案,如果你的项目,能够容忍从头Replay,那就可以跳过了。这里讨论的是,将其他客户端的现场正确地同步给中途加入的客户端这种方案。在最开始的结论部分已经提过,如果游戏玩法比较简单,中途加入还是很好实现的。但如果包含复杂的互动玩法,那对于游戏开发来说,将是类似两万五千里长征似的漫长负担了。中途加入要处理的问题很多,一个很小的功能需求改动,就可能导致整个同步机制挂掉。而且要注意,这个改动带来的同步不一致还不一定是必现的。这就对开发和测试人员提出了极高的要求,必须一点问题都没有,不能有一丁点的bug,一旦出现bug,就是致命的——不同步。不同步不像其他的bug,可以忍受,不同步一旦发生,玩家的所有付出就都白费了,结果不能上传,得不到服务器的认可,还可能会被认为作弊。
刚开始接触帧同步的开发人员,可能觉得,中途加入,就把所有对象的状态(比如:位置)同步给另一端不就行了吗?那这里举几个比较简单的例子,说明一下中途加入的复杂:
碰撞反弹:这就涉及到时序问题,先碰撞的就要先反弹。那么,某一时刻,3个物体碰撞在一起。此刻有玩家B中途加入战局,就需要将玩家A现场同步给玩家B,那如何同步才能保证,在B端玩家下一刻执行反弹逻辑顺序与A一致呢?
碰撞过程:在使用碰撞过程中,经常会依赖某个指定的碰撞过程,比如Enter、Stay、Exit等。以Enter为例,某一时刻,玩家A现场两个物体碰撞触发了Enter过程,并且A端将Enter的回调逻辑处理完毕,则此时A现场的状态就是处理完之后的状态。此时,B加入战局,需要将A现场同步给B,那如果将A当前状态同步给B的话,B端检测到这两个物体碰撞了,又会触发一次Enter,则再次调用Enter的回调逻辑,而A端不会再触发回调。这就导致A与B端不一致。
上面只是举了几个物理相关的例子而已,还有很多其他的会导致不同步的问题,比如跟时间相关的状态等等,这里没有列举。
总之,帧同步的终极问题是中途加入。其他的问题都还好,中途加入会导致后期的每一个功能改动,都可能会带来整个版本的复查,其维护成本之高,可能会令很多团队承担不起。因为我们不能允许中途加入出现bug,一旦出现bug,就是致命的。所以,还是开篇的那句话:如果项目不需求中途加入,帧同步向你敞开大门,如果需求中途加入,请仔细评估玩法和开发之间的矛盾,选择一条更适合的道路。后使用快捷导航没有帐号?
 论坛入口:
  |   |    |   | 
实时对战网络游戏--基于帧同步的最佳实践
客户端&服务器&网络通讯&
image001.png (41.4 KB, 下载次数: 2)
09:19 上传
  文/kylin--zhiruihui
  网络游戏概述
  网络游戏的发展始于90年代。历经超过20年的发展,游戏结构和内容发生了天翻地覆的变化。自2005年以后,网络游戏的结构逐渐趋于稳定。网络游戏从联网特性上,可以大致分为弱联网和强联网两大类。弱联网,如大部分的页游,部分的手游。除此之外的网络游戏,如MMORPG,FPS/TPS,RTS等,都属于强联网。弱联网游戏,结构相对简单,已经有大量前人的文章进行了分析,这里就不再赘述。本文将对强联网游戏进行分析,并针对其中的帧同步方式,进行深入解说。
  强联网游戏,游戏种类比较典型的有MMORPG,FPS/TPS,RTS这3大类。
  MMORPG游戏
  一般使用权威服务器结构,典型的CS架构。游戏使用基于视野控制的区域数据同步,游戏的模拟精度较低,游戏整体延迟容忍度较大。一般单个场景,可以有2000个玩家,单位数量在10000以内,玩家可视同步数量在60-100,同步间隔在500ms。服务器承担游戏核心数据的运算。服务器上通常只有场景的可行走性,区域,触发器等关键数据,无整体空间数据。客户端作为游戏世界的窗口,将游戏内容呈现给玩家,并对玩家输入,进行反馈。游戏运算逻辑的分担设计,主要考虑玩家体验,服务器负载及反外挂需求。如果核心逻辑全部都由服务器计算,则安全性最佳,但客户端玩家操作的模拟,及多客户端之间由于同步延迟导致的画面不一致,将比较巨大。典型如国内武侠类游戏,刀光特效都在1米以上,有效攻击检测基本都在2米以上,这样做是为了就算两个玩家看起来位置很接近,但实际离很远,也能保证命中。不同游戏,主要是在位置移动同步,技能表现,动作表现这块进行调优,但所使用的策略基本大同小异。
  FPS/TPS游戏
  战斗部分一般使用HOST/CLIENT架构。游戏使用全场景活动单位的同步(状态,差值),游戏模拟精度较高,游戏对延迟的容忍度低。一般单个场景,活动单位数量在40个以内,同步间隔在50ms以内。主机承担移动验证及广播同步,命中计算(验证)及广播同步等逻辑,主机有完整的游戏场景数据。客户端负责游戏玩家的可视及操作反馈,移动逻辑一般在客户端直接执行,主机只负责验证。该类游戏为了玩家的操作的体验,甚至可以牺牲部分安全性,将射击等部分关键逻辑,直接在客户端运算执行,服务器只进行后置有效性验证。
  RTS游戏
  一般使用基于帧同步的架构,包含帧同步服务器及玩家端。帧同步服务器,只负责控制命令的队列化及帧数据的广播。每个参与战斗的玩家端,都运行整个游戏世界。游戏使用帧同步技术,游戏的精度高,数据同步量低,游戏场景活动执行的单位数,不受网络同步限制,可以在1000以上。每个玩家,作为对等端参与游戏的执行,玩家的操作将作为控制命令,发送到帧同步服务器,服务器把命令插入某个游戏帧,广播给所有客户端。
  以上大致介绍了3种类型游戏的结构及同步方式。这里对同步方式进行下总结,MMORPG同步60个单位,每秒同步2次。FPS/TPS同步40个单位,每秒同步30次。RTS游戏,同步游戏逻辑帧,不同步游戏单位(游戏单位数量对网络同步量无影响),每秒同步30次。
  3种方式,没有优劣的区别,只有适应某种类型游戏与否。从开发及维护难度上,各有自己所面临的挑战。但帧同步,因为部分特殊性,在3种类型的方式中,在维护难度上可能是最大的。以下我们开始详述帧同步所面对的挑战及解决方法。
  帧同步的挑战
  大家可以想象一下,N个客户端独立运行整个游戏,他们之间唯一的交集,就是有一致的驱动帧(控制命令)。每个客户端的游戏世界独立模拟,在模拟过程中,如果发生了哪怕一个非常细微的差异,在最后的游戏演变及结果上,都会造成巨大的不一致。这个就是帧同步开发及维护最大的挑战。
  在开始说明我们要怎么做之前,这里对帧同步,这个比较模糊的概念,下一个更为明确的定义。我们这里所要讨论的,实际上可以定义为: 基于帧同步的对等计算。所有参与计算的对等端,有一致的开始,一致的变化过程,最终会有一致的结果。
  一致的开始: 游戏有多个玩家,多个阵营。每个玩家,运行整个游戏世界,游戏逻辑相关的所有数据及程序逻辑模块完全一致。
  一致的变化过程:a. 游戏中的逻辑驱动流程完全一致。b. 游戏中逻辑数据变化,完全一致。
  每个参与游戏的玩家,可以说是通过某个特定阵营窗口,对游戏世界进行的观察及操作。游戏世界,所有人都一致,游戏窗口可以有不同的表现。
  以上给出了明确的定义。在设计上,我们可以把游戏拆分为逻辑部分及表现部分。游戏逻辑部分,称为服务端逻辑;游戏表现相关,称为客户端表现逻辑。这里强制加入服务端,客户端概念,是希望让开发者,在进行开发设计时,把这两个作为独立部分进行处理。在编码上,甚至可以把它们放在两个独立工程下。服务端逻辑部分,表示完整的游戏本身,在校验服务器进行逻辑后校验时,也只会执行逻辑模块相关内容。客户端表现部分,会只读使用服务端的数据(本来就在同一个进程内),并通过一个特殊接口发送控制命令给服务端。由此在相同的服务端逻辑基础上,客户端表现可以是2D,也可以是3D,在实际开发中我们做过类似的适配。
  现在可以开始我们的旅程了: 如何确保我们开发的逻辑在所有对等端都有一致的开始及一致的变化。如果发生了不一致,我们如何能检测到。我们需要一个框架来帮助我们解决这些问题,我们先把这个框架命名为IGameKernel。
  游戏一致的开始
  游戏框架服务端逻辑部分,在所有对等端中,根据一致的初始化参数GameStartDocument,进行一致的初始化构造,如场景,对象,逻辑模块等。如此游戏会有一致的开始。
  游戏数据变化一致性
  游戏数据类型,包括int,bool,float,string。其中int,bool,string运算不会有一致性问题。
  对于需要确定性的帧同步,浮点数是一个坑。在不同的CPU架构,不同编译器版本,在乘法,除法,精度控制上,都可能具有一定程度的不一致。虽然理论上,只要编译器及处理器都遵循IEEE754标准,就能够确保浮点数的一致性。但在手游环境下,芯片种类繁多,执行的标准,尤其是对浮点数运算器的优化,都有一定的差异。因此要确保浮点数运算结果的完全一致,就算强制调用类似_controlfp(_PC_24,_MCW_PC);_controlfp(_RC_NEAR, _MCW_RC);代码,也不一定有效果。因此,能不使用浮点数,就不使用浮点数。如果必须使用浮点数,则一定要使用float64。在我们项目中,大约万次级别的测试中,游戏单位数量200以上,平均战斗3分钟,游戏过程及结果都能保持一致。
  浮点数的完全确定性,在手游环境下是难以做到的。但在实际运行环境中,float64精度引起的误差,也许1亿盘游戏里会有一盘直接影响游戏结果。
  另外浮点数相关的函数库,也是一个问题。数学库,如cos,sin等,甚至浮点数fmod,都需要自己实现一套,与环境编译器无关的实例。另外开方等高阶数学计算,因为存在cpu及数学库的依赖性,我们也需要自己手动实现相关函数,并保证结果的一致性。
  其他关于数据运算,如随机函数等,也需要使用框架提供的接口,以保持一致性。
image003.png (18.5 KB, 下载次数: 8)
09:19 上传
  游戏逻辑驱动一致性
  游戏驱动,必须完全由IGameKernel框架完成。帧同步的细节由框架全部隐藏,在用户接口层,完全不体现关于帧的概念,开发者只关注通用意义上的驱动点。在结构设计上,框架把游戏逻辑实体分为GameObject,GameModule,GameObject包含所有数据,GameModule负责逻辑执行。框架提供的逻辑驱动点,只会包含command, heartbeat, event,critical, rechook这些。command:控制命令,heartbeat:心跳,event:框架内部事件,critical:游戏对象属性变更事件,rechook:游戏对象表格数据变更事件。通常的执行流程如下,框架执行某一个逻辑帧,逻辑帧包含两个部分,时间及玩家控制命令。框架会投递玩家控制命令进行执行,然后内部心跳管理器进行心跳更新。心跳更新时,所有注册的心跳被驱动,在心跳执行逻辑时,可以通过发送command执行各种定制行为;当有GameObject属性或表格数据发生变更时,也发送变更事件,并执行相关逻辑。该框架提供的机制强度,足够支持类似WAR3这种复杂度的游戏开发了(实际使用过程中开发过类似复杂度游戏)。
  为了保证执行过程的一致性,框架只是第一步。我们还需要确保游戏逻辑调用是一致的。这里关注几个常见项: 排序算法是稳定一致的,容器遍历顺序是一致的。
image005.png (20.83 KB, 下载次数: 3)
09:19 上传
kernel事件驱动回调
  帧同步一致性检测
  以上我们大致讲解了如何保持帧同步游戏的一致性。但是对于帧同步开发,哪怕开发团队很有经验,也很难避免出现因为人为疏忽,或者经验原因,导致的游戏不一致。此时我们需要一种手段能在最短时间,以最小代价,通过可重现的方式,自动化的帮助我们找到不一致点。
  腾讯王者荣耀团队,有一个后台自动战斗及日志比对系统,该系统能解决部分问题。但这种定位还不够精确。
  我们希望能直接锁定发生不一致的帧,并且精确锁定哪些游戏对象的哪些属性发生了不一致,该帧在哪个调用流程发生了不一致。而且最关键的,我们希望能通过程序,直接调试该不一致发生的过程。满足以上特性的框架,能把帧同步开发最大的挑战,有效解决掉。
  这个是能做到的么,当然可以。这里给出我们的解决方案。
  游戏对象,类体系结构由框架维护,即GameObject的所有类型,属性,表格定义,由框架维护。框架给出类似Get/Set接口,对某一个对象的命名属性进行操作。整个游戏的运行,由框架驱动并检测。框架在每一帧执行完成后,抓取游戏内所有GameObject对象的数据,以及该帧游戏运行驱动流程数据,打包上传到帧同步服务器。帧同步服务器在收集到某帧所有玩家提交的游戏数据后,对数据进行比对,分别比对所有GameObject的属性及表格,并比对所有的事件驱动流。如果发生不一致,则通知玩家端,不一致发生的完整数据信息(把该异常帧,所有玩家的数据都下发)。玩家端收集完异常数据后,游戏结束,并输出游戏录像,同时输出帧同步异常比对文档。文档中打印出游戏内所有游戏对象的数据及游戏运行流程数据,并标注清楚相同及不同标记。
  在项目测试阶段,使用PC模拟端,IOS设备,ANDROID设备,运行自动匹配战斗脚本。当有帧不一致发生后,自动输出录像及异常信息文档。测试完成后,把相关的录像及文档发给项目组,项目组可以安排人员进行问题调查及重现,对复杂问题,也能通过异常录像直接调试。
image007.png (33.36 KB, 下载次数: 4)
09:19 上传
帧同步检测文档
  帧同步的主要关键问题
  帧同步技术挑战与解决方案,到这里可以告一段落。在实际应用场景中,我们通常还会关注以下关键问题: a. 帧同步结构。 b. 游戏重入性。 c. 网络优化。 d. 游戏录像。 e. 反外挂。 f. 第三方库使用。
  帧同步结构
  传统的帧同步,使用锁帧技术。这个技术的要点,是在进行广播某一帧时,在这之前必须收到所有对等端前置的通知应答。这么做的理由只有1个: 保证公平性(大家进程一致,要卡一起卡)。在当前游戏领域,我们更关注的是一个玩家卡了,不能把其他玩家也卡住。因此我们采用乐观帧锁定方式。玩家操作,会以command形式,发送给帧同步服务器,服务器会把command插入某一个逻辑帧frame,然后在合适的时机把frame广播给所有玩家,游戏世界一致执行。在这个结构上,帧同步服务器,是以固定间隔把frame进行广播,某一个玩家卡了或者挂了,对于其他玩家是完全不可见,也是无影响的。
  游戏重入性
  重入性,断线重连及客户端重启后游戏恢复,都是依靠帧同步服务器来保证的。在服务器上,保存游戏开始到当前的所有游戏帧。这样断线重连就很简单了,客户端在网络断开恢复后,请求服务器重新发送从X帧开始的帧。服务器收到请求后,发送X到最新的逻辑帧到客户端,客户端快速运行完成这些帧,然后游戏继续执行。客户端重启也类似,客户端重启后,服务器通知客户端正在游戏中,客户端开始加载游戏,并请求载入游戏帧,服务器发送所有游戏帧,客户端快速执行到最新帧,游戏就可以继续执行了。游戏旁观,玩家中途加入,也采用类似方法。
  网络优化
  帧同步对于网络优化,是一个大问题。这里涉及几个点: 网络延迟及抖动处理,网络同步量及服务器承载优化,玩家操作延迟优化。游戏世界由逻辑帧,直接驱动,在不做优化的情况下,如果网络发生抖动,逻辑帧的到来时快时慢,对于玩家体验而言是致命的。在这里,先直接给出我们的做法: 帧聚合及帧延后执行技术。
  帧聚合可以简单理解为把多个帧,集合起来发送。帧延后执行,大致是本地缓冲部分逻辑帧,按照玩家本地速率执行,把网络抖动抹平。
  关于网络优化,帧同步使用UDP还是TCP, 这个问题是被很多人所讨论的,这里也给大家一个参考结论。帧同步,需要可靠传输,因此网络传输层一定要具有可靠性。另外,部分基于帧同步制作的游戏,还希望要能有最短的用户操作反馈延迟,比如100ms以内。综上,我们简单分析下两个协议。TCP是可靠传输协议,但是TCP协议,对于网络丢包重传机制,是基于网络公平性,总流量较少来设计的,其RTO机制,在发生丢包时,需要较长时间恢复。TCP底层实现过于复杂,无有效参数来直接设置RTT或者RTO超时性,对于减少丢包后的恢复时间,基本无解。UDP是不可靠传输协议,甚至可以认为只是在IP层上做了一个基于分包的简单处理,不对链接管理,传输可靠性进行任何处理。这个也是UDP的优点,纯净无添加。在进行帧同步的底层开发时,也可以使用UDP协议,自己开发链接管理及可靠性传输(这里不需要开发NAT穿透等功能,因此难度一般),并把RTO,RTT按照最快速度恢复策略,定制编码即可。最终实现一个简化的,基于最快速度恢复丢包的,TCP like协议。
  最终,帧同步网络底层,因为都是使用可靠传输协议,使用TCP还是UDP,在框架库中,只是一个初始化参数的问题。接口层可以完全一致,因此对于最终开发用户,TCP还是UDP,只是运行期配置参数的问题。至于到底选哪个,要根据项目情况而定。如果是希望玩家操作延迟最低,则可以配置为使用UDP。如果是希望帧同步服务器负载能力最大化,则配置为使用TCP(用UDP封装简化的类TCP网络库,系统运行期网络消耗及CPU消耗,都会大于原生的TCP接口)。
image009.png (22.41 KB, 下载次数: 2)
09:19 上传
网络服务层服务句柄
  游戏录像
  游戏录像功能,是帧同步天赋属性(其他游戏种类都不具备该属性)。唯一需要关注的,就是录像压缩。在帧聚合中,把多个帧压缩为了一个帧,但这还不够,我们在录像中,只记录存在有效帧的数据。然后再把该录像序列化,再通过类似zip等压缩方式,做整体打包。通过以上方法,类似皇室战争复杂度的游戏,一场战斗,处理过的录像大小,基本能控制在1k字节以内。
  反外挂
  反外挂是一个很大的议题。帧同步结构中,所有数据都在玩家本地,理论上玩家可以任意修改这些数据。这里不讨论传统的加壳及反调试技术。这里讨论在实际开发中,帧同步框架能够通过什么方法来解决该问题。框架能提供至少3种保护: a. 关键数据保护,b. 虚拟化, c. 服务器后验证。关键数据保护可以有很多技术,框架对核心数据,可以做内存加密,内存多拷贝冗余保护等。框架提供虚拟化技术,也是一个不错的选择,部分代码可以在虚拟机(lua)中直接执行,破解难度会增加(前提是资源保护足够)。服务器后验证是杀手锏,验证服务器能运行游戏录像,并直接得出游戏战斗结果,任何作弊都无所遁形。
  因此对于帧同步,反外挂相对是一件比较容易的事情。游戏过程中,玩家作弊只会影响到自己,不会影响到他人。游戏结算时,当服务器检测到玩家之间游戏结果不一致时,通过验证服务器,对游戏录像进行验证计算,很容易就能发现是哪个玩家发生了作弊。
  第三方库使用
  游戏开发中,通常会用到很多第三方库。比如物理引擎,寻路引擎等。在帧同步开发的项目中,逻辑模块所使用的任何库,都需要满足确定性原则,即对于某个输入,必须要有一致的输出。因此对于第三方库的使用上,一定要慎重。而这也潜在的增加了项目开发及维护的成本。比如绝大部分物理库,寻路库,如havok,kynapse就无法直接使用。
image011.jpg (178.9 KB, 下载次数: 5)
09:19 上传
笑脸-来源网络
  帧同步总结
  到这里,关于帧同步的开发已经讲解的差不多了。帧同步技术很容易用,但要用好,却不是那么简单,而且如果用不好,对项目甚至可能是致命的(有做射击游戏的,使用帧同步,项目死在物理库上的)。本文将实践中,遇到的最常见的挑战与解决方案,与大家进行了分享。如果在开发中碰到困难,可以联系我们进行咨询。如果本文所阐述的内容有谬误的,也欢迎指出。
声明:游资网登载此文出于传递信息之目的,绝不意味着游资网赞同其观点或证实其描述。
关注我们官方微信公众号
下载我们官方APP-游戏行
关注手游动态微信公众号
请原谅在下的愚钝,一直以为帧同步这种东西,很猥琐,也很不理解,什么人想出来的这种招;
不知道帧同步一秒钟是不是干几十个包到对方去,这种包速会不会极大的拖慢服务器,求解释;
LifeRoad 发表于
请原谅在下的愚钝,一直以为帧同步这种东西,很猥琐,也很不理解,什么人想出来的这种招;
不知道帧同步一 ...
使用帧聚合技术。发包速率根据游戏操作复杂度(如皇室战争),可以自动适配优化到1秒2个包以内。一台普通双核虚拟机,可以支持至少2W人同时对战。
zhiruihui 发表于
使用帧聚合技术。发包速率根据游戏操作复杂度(如皇室战争),可以自动适配优化到1秒2个包以内。一台普通双 ...
请教下楼主,帧聚合1秒2个包 会不会导致玩家感觉操作反应延迟啊,按下个按钮,几百毫秒才有反应
逍遥丿小宝 发表于
请教下楼主,帧聚合1秒2个包 会不会导致玩家感觉操作反应延迟啊,按下个按钮,几百毫秒才有反应 ...
常规理解是会导致这个问题。但是有优化方法,操作提交给服务器后,服务器立刻做插值帧,然后直接广播,可以把延迟极大降低。另外,根据不同的游戏类型,游戏可容忍的操作延迟从50ms -- 500ms都有,通过客户端前置表现处理,优化手感,使玩家体会不到延迟感。
学习了。。。
游戏版号7月将下放一批 同时游戏公司资质审吉比特、飞鱼继续投资TapTap 估值增长至近2斗鱼直播一姐冯提莫继卢本伟后再次被“央视你还能在游戏行业混多久?MMORPG《楚留香》手游架构首次深度分析手游赚了3亿美元奶粉钱后,他们又做了一款
微信扫一扫关注我们→

我要回帖

更多关于 如何实现聊天消息同步 的文章

 

随机推荐