帧同步在网上可以搜的资料比较少,关于游戏的更是没有,不过,实现的原理也比较简单,最近几天就写了份关于帧同步的文档,当作给同事扫扫盲,顺便也在这里发发,可以给其他人参考参考
--竞技类网络游戏设计方案
一、 前言
帧同步,根据wiki百科的定义是,一种对同步源进行像素级同步显示的处理技术,对于网络上的多个接入者,一个信号将会通过主机同步发送给其他人,并同步显示在各个终端上。同步信号可以是每帧的像素数据,也可以是影响数据变化的关键事件信息。
帧同步在网络游戏中的应用,设计上有异于传统的mmorpg游戏,因为可以承载更大量的后台计算,实现类单机的效果,所以可应用在类似射击类、飞机类中实现弹幕计算或者格斗类的高精度打击体验
本文将主要介绍下帧同步与传统mmorpg设计框架的异同点以及相关的几个设计方案,最后,深入展开对其中一种实现方案的分析,而相关的反外挂和断线重连机制等技术难点暂不在本文讨论。
二、 帧同步在游戏中的应用
网络游戏中,游戏服务的架构大致可以分为2种模式,分别是cs模式和p2p模式
cs模式框架如图1(c为客户, GSS为游戏状态服务器)
图1
如图1,游戏状态服务器(GSS)单独部署,负责对网络上各个接入者提供服务,当GSS状态发生变化时,将状态同步发送给各个接收者。
p2p模式框架如图2(c为客户,GSS为游戏状态服务器):
图2
图2中,游戏状态服务器存在于各个客户主机上,游戏状态的改变直接来自于各个客户端的输入。
以上2个服务框架中,cs模式,由于GSS服务器只有一个,游戏状态能保证绝对一致,但GSS可能同时服务上万个玩家,由于机器性能以及网络带宽等硬件资源限制,服务器对大部分情况都无法进行非常严格的检查和处理;p2p模式相对于cs模式,同时连接的玩家有限,所以可以进行比较精细的运算,可实现类似射击类、飞机类的弹幕计算或者格斗类的高精度打击体验,但是,由于端到端的通讯方式,随着同时接入用户的增加,通讯量呈指数级增长,所以,其对同时接入的数量上会限制得比较严格,适合少量同屏的竞技类等游戏。
p2p模式中,由于存在多份的GSS,如何保证各个GSS一致也需要特殊考虑, 帧同步算法在游戏中的应用,主要就是为了解决p2p模式下的GSS一致性问题。实现原理是将游戏处理细化为帧,对于每帧,在同样的运行环境中,保证同样的输入的情况下,将得到同样的输出结果。
图3
图3中,初始状态都为1,序列帧第二帧时,输入加1操作,则状态变为2,第三帧时无输入,状态不变,第四帧时,输入加1操作,状态变为3.对于同个运行环境的各个客户端来说,相同的输入状况下,将得到相关的输出结果,如图4效果。
图4
通常,为了用户的输入能及时的响应以及游戏状态的过度能够平滑,会将GSS设置为20到30帧以上。并且,由于客户端机器性能或者设置的差异,GSS的状态无法与游戏渲染帧实现一一对应,所以,GSS与表现层必须做到完全的分离,否则将因为某些细小的误差被放大最终导致游戏出现完全不同的结果。
图5
如图5,非确定的渲染层的输出,完全由GSS来驱动,GSS保证帧数的稳定,即使出现网络延迟,也必须在确保收到该帧的所有输入后才执行该帧的处理。
实现方案上,大致可以分出3种,分别是无主机结构、有主机结构、服务器主机结构
u无主机结构
在图2的拓扑结构中,所有GSS功能对等,该方案需要进行特殊的对帧处理,确保所有客户端都已经同步并且收到所有的输入。但是,由于网络上的各个客户端完全对等,一旦某个用户网络状况出现延迟或者中断等异常,将影响其他用户的操作体验,所以该方案简单公平但体验容易受限
u有主机结构
图6
如图6,在各个客户端中随机选择一个的GSS作为主机,同时负责对帧控制及输入输出管理,其他GSS仅跟GSS主机通讯,GSS之间互相不通讯。该方案的好处是,游戏的体验只受主机与本机的网络与本机器状况的影响,其他GSS出现的任何故障都不会影响其他人,当GSS主机完全失去联系时,其他GSS也可以重新仲裁得出新的GSS主机来,但该结构主机在客户端,容易给外挂有可乘之机,对输入对帧等能进行特殊处理,最终导致游戏丧失公平性。此方案能保证玩家体验,但安全性较低
u服务器主机结构
服务器主机结构,是将图6的结构中的GSS主机的的对帧控制及输入输出管理功能放在服务器上,降低GSS客户端的客观影响,保证了大部分玩家的体验,且其中有玩家作弊,也能马上检测到,保证游戏的公平性,但结构上已脱离p2p设计,通讯流量随用户增加,负额指数级增长。该方案安全性高,保证玩家体验,但对服务负载有一定的要求。
u其他
融合有/无主机与服务器主机的结构。服务器主机结构的特点在于控制权在服务端,在有状态的网络游戏中,可以有效防止游戏数据修改、游戏加速等外挂,在服务端硬件资源方面,可以增加有/无主机结构减轻负担,大部分功能用有/无主机结构处理,关键操作由服务器主机结构处理等,让GSS主机与服务器主机协同服务
三、 服务器主机结构设计
服务器主机结构的特点如上所述,这里再深入展开对该结构的分析与设计。
服务器设计
图7
服务器主要是起到控制作用,进行客户端的对帧控制和输入输出管理。如图7,服务器每帧都发驱动帧驱动客户端执行帧处理,当客户端有输入被服务器接收到,则服务器当前帧内将输入同步输出给各个客户端.
网络上由于客户端的状况多种多样,客户端帧数可能跟不上服务器,如图8所示,如果客户端出现掉帧情况,则在收到驱动帧后需要加速执行,以追上其他客户端的速度,避免掉帧的用户一直在对过去的事件进行响应。
游戏应该优先保证正常用户的体验,所以当有玩家出现卡帧情况的时候,不应选择暂停其他玩家,而是让他慢慢的追赶上来,设计上,服务器即可以采用客户端的正常速度,按帧驱动客户端,但当网络都出现突发状况的时候,如图9,通讯异常时,2个客户端都对帧数2缺失,如果服务器照常运行,到恢复网络状况时,会出现情况是,每个客户端都卡了几帧之后,加速拉了几帧。所以,针对这种情况,增加客户端的对帧操作,即客户端执行第1帧时,跟服务器说可以播放第二帧了,然后服务器开始驱动第二帧动作,考虑网络延迟情况,可以提前对帧第n帧的,效果如图9,左边客户端第二个对帧操作使服务器开始推动第二帧进行,而右边客户端的第二个对帧动作其实不起任何作用
图8
图9
伪代码
代码不贴了
客户端设计
图10
客户端设计由两部分组成,分别是GSS模块和渲染模块。
GSS模块包含物品系统、角色系统、AI系统、场景系统还有其他相关系统等,同时,输入输出和帧数控制也一起集成在GSS模块中。GSS中各系统功能分别是:
物品系统: 游戏物品以及物品的效果
角色系统: 角色包括玩家角色、npc及apc等
ai系统: 驱动apc行动的控制模块
场景系统: 场景物件、地图、寻路等
其他系统: 其他类似技能、状态等系统
输入输出模块: 监听玩家输入,将玩家输入上报服务器,同时监听服务器输入,绑定当前帧输出
帧数控制模块: 监听服务器驱动帧,驱动执行每帧处理
GSS模块中各个系统的执行,由帧数驱动,不引入其他时间线。有如物品持续时间、状态持续时间等都以帧数作为唯一的时间轴。帧与帧之间的播放频率,则由服务器统一控制,但由于网络抖动等影响,帧的频率并不是太稳定,为避免播放抖动,帧数控制器需要进行一定的平滑处理。
图11
客户端的渲染层,由GSS模块驱动,为减少模块间的耦合,GSS模块使用事件通知机制驱动渲染层表现。具体细分事件类型如图12(具体项目具体事件拆解)
由于渲染层与GSS只做到事务级的同步,而GSS与渲染层的播放速率有可能不同,则为保证较好的表现效果,GSS的逻辑帧需要与渲染层的渲染帧做固定比率的绑定,譬如图13的1:2,当GSS逻辑帧数不变的情况下,渲染帧掉帧时,能经过换算得到当前逻辑帧对应的渲染帧数,出现GSS帧数暂停时,则逻辑帧也跟着一起暂停
图13
逻辑帧与渲染帧绑定算法(伪代码)
代码不贴了
其中 OnUpdate由引擎在每帧调用,GetNewestFrame获得逻辑帧通知过来的最新帧,这样,保证了逻辑帧中关键帧进行伤害计算时,渲染帧不会脱帧严重。
四、 反外挂与断线重连
稍等后续文章
对于一个游戏来讲,战斗就是灵魂,如果战斗做不到极致,其他方面做的再好也是徒劳,这几年,也参与了很多游戏的开发,其中有很多游戏是从决策到死掉全程参与,深有感慨。从端游到页游 从页游再到手游 每一个新市场机会出现的时候,都是从闭着眼睛就能赚钱到大部分赚不到钱过度,对游戏的开发技术都要求也是越来越高,只不过每个市场到成熟的时间都被大大缩短了。对于战斗来讲很多都是策划脑补的跟实际做出来的完全不是一回事,很多原因都是战斗方案选型就是错误的。
从程序角度来讲,我把战斗从两个维度分类:
1、从操作方式上分为回合操作、即时操作。
2、从交互方式上分为 离线战斗 联网战斗,这个地方需要说明一下,有些游戏虽然也能进攻别人,例如COC 但是因为战斗的时候,另外一个人是不可以操作的,类似于这样的战斗也可以称为离线战斗。
基本上所有的战斗都是以上两种方式在某种程度的组合而已,例如梦幻西游可以认为是联网战斗、回合操作类型。
最近比较火的全民超神,王者荣耀,属于 (联网战斗|离线战斗)即时操作类型。
最新网易出的功夫熊猫 属于 (联网战斗|离线战斗)即时操作类型,相对于dota类的全民超神、王者荣耀,他对延迟要求的更高了。
如果在立项初期,项目计划时候不确定游戏的操作类型,以及网络要求,做得后期要想调整的话,改动是致命的,假设按照之前时空猎人的方式实现的纯离线战斗及时游戏,最多也只能做做离线PVP,如果想增加联网PVP的功能的话,对于程序来讲几乎需要重写战斗。
随着移动游戏市场越来越成熟,对于战斗的要求也在提高,原来做一款ARPG,只有单机玩法就足够了,不需要开发实时PVP,但是现在市面上的ARPG不仅可以联网PVP,甚至可以联网组队PVE了,所以我感觉如果现在再去做游戏的话,只是一个单机玩法,或者是离线PVP玩法已经远远不能满足现代玩家的口味了。
我认为现在战斗系统需要满足一下几点。
1、一定要有离线PVE玩法,或者离线PVP玩法,可以在让玩家在网络不好的时候消遣,节省流量。(全民超神、王者荣耀在5V5匹配时候都有一定几率匹配到离线战斗,这个时候是不耗流量的,其他人全是AI控制的)
2、一定要有在线PVP,在线PVE,能够让玩家在网络比较好的时候,实时竞技。增加可玩性。
3、战斗中,尽最大程度节省玩家的流量,例如全民超神这款游戏,一场30分钟的战斗基本上要消耗掉20M的流量,而且此类游戏大部分是玩的联网战斗,基本上在非wifi情况下没法玩。
4、需要有战斗回放机制,可以让策划设计离线玩法的时候更自由,例如COC,战斗回放基本变成了它游戏的一部分。
5、防作弊,如果有离线玩法的话,一定有机制对离线玩法的结果进行验证,要不然等你游戏真火了,你就知道错了。
6、实现难度相对较低。
对于联网游戏来讲,同步的方式主要分为两种,状态同步、帧同步。
1、状态同步:顾名思义,是指的将其他玩家的状态行为同步的方式,一帮情况下AI逻辑,技能逻辑,战斗计算都由服务器运算,只是将运算的结果同步给客户端,客户端只需要接受服务器传过来的状态变化,然后更新自己本地的动作状态、Buff状态,位置等就可以了,但是为了给玩家好的体验,减少同步的数据量,客户端也会做很多的本地运算,减少服务器同步的频率以及数据量。
2、 帧同步:RTS游戏常采用的一种同步技术 ,上一种状态同步方式数据量会随着需要同步的单位数量增长,对于RTS游戏来讲动不动就是几百个的单位可以被操作,如果这些都需要同步的话,数据量是不能被接受的,所以帧同步不同步状态,只同步操作,每个客户端接受到操作以后,通过运算可以达到一致的状态(通过随机种子保证所有客户端随机序列一致),这样的情况下就算单位再多,他的同步量也不会随之增加。
下面我们从以上的5个方面对各自实现方式进行描述:
总结一下:
1、对于回合制战斗来讲,其实选用哪种方式实现不是特别重要了,因为本身实现难度不是很高,采用状态同步也能实现离线战斗验证。所以采用帧同步的必要性不是很大。
2、对于单位比较多的RTS游戏一定是帧同步,对于COC来讲,他虽然是离线游戏,但是他在一样输入的情况下是能得到一样结果的,所以也可以认为他是用帧同步方式实现的战斗系统。
3、对于对操作要求比较高的,例如MOBA类游戏有碰撞(玩家、怪物可以互相卡位)、物理逻辑,纯物理类即时可玩休闲游戏,帧同步实现起来比较顺畅,(有开源的Dphysics 2D物理系统可用 它是Determisti的)。
4、对于战斗时大地图MMORPG的,一个地图内会有成千上百的玩家,不是小房间性质的游戏,只能使用状态同步,只同步自己视野的状态。
5、帧同步有个缺点,不能避免玩家采用作弊工具开图。