帧同步原理

帧同步原理

参考文章链接如下:

https://blog.csdn.net/SilenceCoder/article/details/86234480

https://www.gameres.com/774540.html

https://blog.csdn.net/wangchewen/article/details/120511500

常见游戏同步方案

  同步问题的本质是一致性的问题,在同一局游戏的过程中,玩家A看到玩家B的状态,应该跟玩家B自身看到自己的状态相一致。延迟是造成不一致的本质原因,假设理想情况下双方的网络时延都为0,那两者应该是同步的,但是在现实情况中,往往是不可能的,本文讨论的同步机制,就是为了解决一致性问题而产生的,对于不同的游戏类型,不同的团队技术积累,可以根据自身情况采取不同的同步机制技术方案。

状态同步

  状态同步主要指客户端发送游戏动作到服务器,服务器收到数据后对游戏行为结果进行计算,然后通过广播下发游戏的各种状态到客户端,客户端收到状态信息后进行UI层展示或处理。这种方式列类似于核心逻辑计算都在服务器端进行,客户端主要负责上传操作动作等请求数据,一般回合制游戏主要采用这种方式。

状态同步基础模型

效果图

帧同步

  客户端发送游戏动作到服务器端,服务器端广播转发所有客户端的动作(或者客户端直接通过P2P技术发送),客户端根据收到的所有游戏动作来做游戏运算和显示。这种做法等于客户端之间互相远程控制其他客户端上的游戏软件。早期的IPX网络游戏,如红色警戒、帝国时代,星际争霸,甚至大量的支持网络连线双打的游戏机模拟器都是这种方式。

帧同步基础模型

效果图

帧同步技术基础概念

基础概念

  相同的输入 + 相同的时机 = 相同的显示

  意思是如果我们的游戏,接受了来自网络的多个客户端的操作,如果这些操作在各个客户端是一样的,那么多个客户端的显示也就是一样的了,这就带来了“同步”的效果。所以在这种情况下,各个客户端的运算要绝对的一致,不能依赖诸如本地时间、本地随机数等等“输入”,而要一切以网络来的操作数据为主。

效果图

MOBA类游戏基本服务器架构

  一般的方式是玩家先登录大厅服务器,认证成功后选择组队功能会通过Match Server进行玩家匹配,匹配成功后单独建立一个游戏房间服务器进行对战。

效果图

通信方式

  游戏网络通信方式主要有http和socket两种方式,但http底层也是采用socket实现,区别是http每次通讯完毕都会断开连接,这种对于频繁交互的应用效率较低,所以对于网游客户端一般http主要用于非高频低实时类通信,如登录请求、局外物品购买,签到等。所以游戏实时性要求较高的高频交互部分都是采用socket方式通信。

  socket通信又分为两种,分为TCP和UDP。具体采用哪种socket类型需要结合具体游戏类型来定,以下为两种协议的对比。

对比项TCPUDP
连接有连接无连接
可靠性可靠、有序不可靠、无序
数据包自动拆包手动拆包
流量流量控制手动控制
速度
控制简单复杂

  通过上图可以发现,对于socket我们想做的事情TCP都帮我们处理了,我们需要做的就是建立连接,然后调用socket通信函数收发(recv、send)数据即可,如果采用UDP的话需要我们自己处理这些东西。简单地说就是自己对UDP进行处理模拟TCP,看到这里大家可能以为一定会采用TCP来进行通信,事实是基于游戏场景不同使用的通信协议也可能会不一样,例如王者荣耀是采用UDP来进行通信,腾讯的多数长连接手游也主要是采用UDP。

从《王者荣耀》谈游戏的帧同步

  借助王者荣耀,我们来聊聊其中的技术实现以及同步方式,更多的从MOBA(多人在线战术竞争游戏)方向来解析推理王者荣耀的实现方案。以下是主要讲解的几个重点:

  • 服务器架构
  • 通信方式
  • 同步方案
  • 断线重连
  • 技能同步

服务器架构

  不难发现,王者荣耀的服务器采用房间模式,每个玩家登录以后进入大厅,随后进行匹配游戏。匹配完成之后,把一起对战的玩家放到一个房间内进行对战。

效果图

  房间类玩法和MMORPG有很大的不同,在于其在线广播单元的不确定性和广播数量很小,而且需要匹配一台房间服务器让少数人进入一个服务器。

  这一类游戏最重要的是“游戏大厅”的承载量,每个“游戏房间”受逻辑所限,需要维持和广播的玩家数量是有限的,但是“游戏大厅”需要维持相当高的在线用户数,所以一般来说,这种游戏需要做“分服”的。而“游戏大厅”里面最具有挑战性的任务,就是“自动匹配”玩家进入一个“游戏房间”,这需要对在线玩家做搜索和过滤,以及为了更多的体验,会对玩家进行分地区进行匹配,以方便活得更快速的同步。

  一般的方式是玩家先登录“大厅服务器”,然后选择组队游戏的功能,服务器会通知参与的所有游戏客户端,新开一条连接到房间服务器上,这样所有参与的用户就能在房间服务器里进行游戏交互了。

通信方式

  与上述叙述的相同。

同步方案

  游戏中常见的同步方案,有状态同步和帧同步,一般大型的MMOARPG都是采用状态同步,比如魔兽世界。状态同步采用C/S架构,所有的状态由服务器来控制,安全性比较高,但是流量比较大。帧同步采用的是囚徒模式,所有c端强制采用一个逻辑帧率,从而保证输出一致,其特点是流量小,安全性比较差。

  王者荣耀采用的就是帧同步,那么具体帧同步是什么,如果实现的,我们从两个地方来分解:

帧率

  什么是帧率,可能没有做过client的同学不是很清楚这个术语,我们从一个小李子来讲解一下。我记得小时候有一种小人书,快速翻看就可以看到漫画上的人物会动起来。

  由于人类眼睛的特殊生理结构,如果所看到画面之帧率高于每秒约10-12帧的时候,就会认为是连贯的,此现象称之为视觉暂留。这也就是为什么电影胶片是一格一格拍摄出来,然后快速播放的,就像上图快速翻小人书一样。

  游戏中所有动画也是采用这种方式来渲染的,只不过帧率是由GPU来控制,你所看到的画面都是由GPU一帧帧渲染的,比如30帧/s,你所看到的画面就比较流畅了。而帧率越高你所看到的画面就越流畅。

Lockstep——帧同步

  帧同步可以说是通过帧率延伸过来的,你可以把一个游戏看成一个巨大的状态机,所有的参与者都采用同一个逻辑帧率来不断地向前推进。

  我们看如下两个图:

效果图

效果图

  图中是A、B、C三个玩家的时间轴,这个时间轴不是电脑上的本地时间,而是A、B、C联机时定义的一个时间轴。虚线分割出来的时间片称为turn,可以理解为一帧。箭头表示该玩家将自己的操作指令广播给其他玩家。

  我们把一盘游戏看出看成一个大型的状态机,因为大家玩的是同一款游戏(F),因此F是相同的,初始化状态S0也是相同的。在第一个turn结束时,所有玩家都接收到了完全一样的输入I,注意这里的I不是一个值,而是包含了当前游戏中所有玩家的操作指令集合。t1时刻所有玩家的电脑自行计算结果。由于F、S0和I是固定的,所以每个玩家电脑上计算出下一个状态S1一定是相同的。

  所以通过上面我们可以知道:

  1. 我们把游戏的前进分为一帧帧,这里的帧和游戏的渲染并不是一个概念,只是借鉴了帧的概念,自定义的帧,我们称为turn。游戏的过程就是每一个turn不断向前推进,每一个玩家的turn推进速度一致。
  2. 每一帧只有当服务器集齐了所有玩家的操作指令,也就是输入确定了之后,才可以进行计算,进入下一个turn,否则就要等待最慢的玩家,之后再广播给所有的玩家。如此才能保证帧一致。
  3. Lockstep的游戏是严格按照turn向前推进的,如果有人延迟比较高,其他玩家必须等待该玩家跟上之后再继续计算,不存在某个玩家领先或落后其他玩家若干个turn的情况。使用Lockstep同步机制的游戏中,每个玩家的延迟都等于延迟最高的那个人。
  4. 由于大家的turn一致以及输入固定,所以每一步所有客户端的计算结果都是一致的。

我们来看看具体的执行流程:

效果图

  上图中我们可以明显地看到,这种囚徒模式的帧同步,在第二帧的时候,玩家1有延迟,而导致第二帧的同步事件发生延迟,从而导致所有玩家都在等待,出现卡顿现象。

乐观锁&断线重连

  囚徒模式的帧同步,有一个致命的缺陷就是若联网的玩家有一个网速慢了,势必会影响其它玩家的体验,因为服务器要等待所有输入达到之后再同步到所有的c端。另外如果中途有人掉线了,游戏就会无法继续或者掉线玩家无法重连,因为在严格的帧同步的情况下,中途加入游戏从技术上来讲是非常困难的。因为你重新进入游戏之后,你的初始状态和大家不一致,而且你的状态信息都是丢失状态的,比如你的等级、随机种子、角色的属性信息等。比如玩过早期的冰封王座都知道,一旦掉线基本这局就废了,需要重开,至于为何没有卡顿的现象,因为那时的解决方案都是采用局域网的方式,所以基本上是没有延迟问题的。

  后期为了解决这个问题,如今包括王者荣耀,服务器会保存玩家当场游戏的游戏指令以及状态信息,在玩家断线重连的时候,能够恢复到断线前的状态。不过还是无法解决帧同步的问题,因为严格的帧同步,是要等到所有玩家都输入之后,再去通知广播client更新,如果A服务器一直没有输入同步过来,大家都要一致等着,那么如何解决这个问题?

  采用“定时不等待”的乐观方式在每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新。如此帧率的时钟在由服务器控制,当客户端有操作的时候及时的发送给服务器,然后服务端每秒钟20-50次向所有客户端发送更新消息。如下图:

效果图

  上图中,我们看到服务器不会再等到搜集完所有用户输入再进行下一帧,而是按照固定频率来同步玩家的输入信息到每一个c端,如果有玩家网络延迟,服务器的帧步进是不会等待的,比如上图中,第二帧的时候,玩家A的网速慢,那么他这个时候,会被网速快的玩家给秒了(其他游戏也差不多)。但是网速慢的玩家不会卡到快的玩家,只会感觉自己操作延迟而已。

技能同步

  游戏中有很多是和概率相关的,比如说技能的伤害有一定概率的暴击伤害或者折光被击等。按照帧同步的话,基于相同的输入,每个玩家的client都是独立计算上海的,那么如何保证所有电脑的暴击伤害一致?这个时候就需要用到伪随机了。

  大部分编程语言内置库里的随机数都是利用线性同余发生器产生的,如果不指定随机种子(Rand om Seed),默认以当前系统时间戳作为随机种子。一旦指定了随机种子,那么产生的随机数序列就是确定的。就是说两台电脑采用相同的随机种子,第N次随机的结果是一致的。

  所以在游戏开始前,服务器为每个玩家分配一个随机种子,然后同步给client,如此每个client在计算每个角色的技能的时候,就能保证伤害一致的。这也是多数帧同步游戏采用的方案,包括王者荣耀。

影子跟随算法:FPS游戏中游戏同步性的实现

  何为延迟补偿?如何进行坐标插值?B客户端屏幕上A已经跑到东边了,但是收到服务器说“A正在西边往北跑”,B到底该何去何从?我若干年前的一个实现版本,将简明扼要的解决这个问题:

  影子跟随算法由普通DR(dead reckoning)算法发展而来,我将其称为“影子跟随”意在表示算法同步策略的主要思想:

  屏幕上现实的实体(entity)只是不停的追逐它的影子(shadow)。

  服务器向各客户端发送各个影子的状态改变(坐标,方向,速度,时间)。

  各个客户端收到以后按照当前重新插值修正影子状态。

  影子状态是跳变的,但是实体追逐影子是连续的,故整个过程是平滑的。

效果图

  前面的1号终端控制红色飞船P1向左飞,并把自己的状态实时告诉服务器。

  后面的2号终端上接收到飞船P1的影子S1的状态(向左移动),并让P1的实体追赶S1。

  网络性能指标一:带宽,限制了实时游戏的人数容量。

  网络性能指标二:延时,决定了实时游戏的最低反应时间。

  使用该算法可以容易的开发出一款马里奥赛车,或者Counter Strike,详细说明如下:

算法比较:

  帧间同步:不同客户端每帧显示相同的内容,键盘/时钟数据传到服务器,服务器确认后阿所有终端做出响应,多用于局域网游戏,比如红警(需要等待客户端),街霸II的网络版(360),可参考LockStep,TimeWrap算法,网速要求高,复杂度低,可见如下文章(帧锁定算法):http://www.skywind.me/blog/archives/131

  插值同步:不同客户端显示不同步,但是状态同步,常见的Dead Reckoning(或叫导航插值),效果好,但复杂度高。常见于竞技类游戏和FPS游戏。

算法定义:

时间:单位为帧(FPS = 10),开始由服务器告诉向所有客户端,每5分钟同步。

玩家:每个玩家控制自己的实体,并在每帧将状态改变告知服务器。

状态:状态数据 = 实体ID + 坐标 + 方向 + 速度 + 时间(帧)。

插值:收到新状态后将根据其运行方向与时间,根据现有时间计算新状态。

跟随:实体不停的追踪自己的影子,追上后与影子保持状态同步。

相位滞后:可选参数,实体与影子保持一定距离同步,相当于保持一定车距,这样在控制者突然停止的时候,不容易因为网络延迟跑过了又被拉回来。

惯性移动:可选参数,开始移动或者停止或者改变方向都有加速度,这样就不需要相位滞后了。

每次服务器向各个客户端同步时间的时候,由于延迟,所有客户端的时间都是慢于服务器的,这没有关系,只要大家在一定误差范围内以相同的速度增加,就完全没有问题。

效果图

IDC网络响应

  在公网平均120ms的Latency下,是不存在“完全的”同步情况的。如何通过消除/隐藏延时,将用户带入快速交互式实时游戏中,体验完美的互动娱乐呢?

  让所有用户屏幕上表现出完全不同的表象是完全没有问题的。把这些完全不同表象完全柔和在一个统一的逻辑中也是完全没有问题的。需要根据具体情况,分清楚哪些我们可以努力,哪些我们不值得努力,弄明白实时二手拍卖平台游戏中同步问题的关键所在,巧妙的化解与规避游戏,最终在适合普遍用户网络环境中(200ms),实现实施快速互动游戏。

案例解析:Counter Strike

  实现CS的话,首先我们需要给人物移动加上惯性,比如静止状态突然开始移动,那么需要0.5-1秒的加速过程,而移动中突然停止也需要0.5-1秒的减速过程,这样就实现了无差别同步,不需要相位滞后来避免拉扯来影响用户感。

  同时开枪射击采用客户端判断,也就是说如果我看见你在墙前面,开枪射中,那么我向服务器发送“我击中你了”,这时有可能真实的你在墙后,那么表现出来的就是我看见我打中你了(减不减血由服务端判断),而你没有看见我,觉得我穿墙打中你了。

效果图

CS的同步逻辑

  关键状态进行缓存,不然如果别人向前连续跳五次,每次取得状态都取到最高点的话,别人客户端上的影子和跟随的实体会奇怪的持续的飞在天上,所以需要将起跳和落地这两个关键状态缓存,实体追赶时只有追上的第一个状态(一号影子)才能追逐第二个状态(二号影子)。

  由此可以在完全时间同步的情况下平滑的跑动、跳跃,开枪射击采用客户端判断后手感得到提高,唯一需要担心的就是外挂,外挂多是实时游戏的代价,只能通过Cheating Death等工具进行防止。

案例解析:马里奥赛车

  用该算法实现马里奥赛车是很简单的,影子和实体都使用惯性,由于赛车惯性很大,不容易有突变的状态更新,所以效果会比FPS游戏更好。

  玩家碰到道具后,马上在屏幕上隐藏该道具的显示并通知服务器,由服务器决断道具属于谁,由于刚碰到道具就隐藏,所以不会出现碰到道具却在一段时间内无法获取的延迟现象。

  游戏道具系统实现也很容易,比如那个将当前第一名炸毁的道具,它的描述是:原角色+对象角色+约定发生时间。既然知道对象是谁,什么时间发生,那就根本不需要怎么同步了,所有客户端和服务器在该时间让炸弹爆炸就得了,这种手法类似即时战略游戏。

  游戏还有一类道具是可以发射的乌龟壳,这个东西属于有弹道的发射物,类似Quake里面的某些武器,需要作一些同步处理,基本特性是服务器判断起决定作用,客户端同步判断,如果客户端与服务器都判断集中,那就集中;如果客户端判断集中而服务器判断没有集中,那会看到该角色似乎被打了一下,但很快又恢复了速度向前冲。

  由于赛车本身就具备惯性比较大的特点,因此同步效果是比较好的,可以在更大的延迟情况下表现得和FPS差不多(比如300ms效果相当于FPS的200ms)。

  该“影子跟随算法”支持非可靠传输协议,如果使用非可靠传输,那么我们按照特定频率(如每秒10次)定时发送状态更新,因为协议中每个更新包除了位置外还有速度、方向和时间,甚至可能还需要加速加速度,因此我们丢一个包没有关系,可以根据后来的包重新计算插值。只有关键状态更新时才需要可靠传输,这就避免了TCP中丢包时RTO指数增长造成的延迟了。

负面情况

  该算法缺点就是无法向“帧间同步”算法那样,每次发送按键给服务器,服务器处理后再反馈结果,在局域网中(平均延迟<5ms),这样的效果相当于单机游戏一样即时,游戏性能很复杂。然而在Internet中(平均延迟130ms,设计基准200ms,每秒最多发送10个数据包)该算法却不能像单机游戏那样有复杂的场景互动,有类似格斗游戏的即时的动作判定。

  许多策划在设计实时动作游戏时很多设计我们都难以实现,这样因为策划不容易明白哪些我们能做,哪些我们不能做。即便程序员精通同步理论,策划也经常碰壁。当多数设计被程序员回复“无法实现”后,策划只有采取一种消极设计(砍掉很多有意思的互动元素),于是网络游戏的表现力到今天还是差单机游戏一大截。

  这些问题也并不能因为“影子跟随算法”的提出而得到改进,大于100ms的判定时间,都很难做到即时。

  最后,该算法编码复杂度比其他同步策略高,因为服务器需要计算一份影子数据,各个客户端需要计算一份影子数据,还需要计算实体追赶,而这三种计算都需要在同样的时钟下保持一致,这就增加了编码与调试的复杂度。

总结话题

  Internet特点是“高带宽,高延迟”,可以说从本质上Internet就不是为了游戏而设计的。故此Internet绝对意义的同步是不存在的。“影子跟随算法”的核心思想有几个:**时钟同步,客户端先行,平滑追赶。**通过这三个特性,我们能够在近似时间同步的情况下,模拟各种物体的移动过程,而使用该算法的前提是设计者需要根据各个游戏的特性研究不同的优化技巧,策略因游戏而变。

  比如发送状态更新包时,不需要每次都发送,而可以只发送改变的状态。什么时候我们觉得改变了?就是当客户端实体与自己的影子之间的误差大于某特定数值时我们才发送更新包,这样虽然玩家在原地做左右摇摆的小幅度移动,只要没有超出范围,都不需要发送新的状态更新,其他玩家机器上看起来,它是站着不动的。

  比如当发现某客户端5秒钟没有相应了,那么就将该人物的影子冻结住,永远不要为了等待某个数据而不让游戏进行下去。

  本算法需要客户端与服务器维护相同的时钟,当每5分钟同步的时候,直接根据服务器的时钟替换当前时钟就行了,不需要重新计算所有影子的位置,因为后续的状态数据将会马上刷新这些状态。更不需要将测量到的PING值考虑进去,该算法与PING具体值无关。

  当发现策划点子不可行时,寻找近似替代方案,比如减少“一次性的”“决定性的”事件发生,比如延长导弹在空中飞行的时间,比如将敌人加入HP分多次打死,而不是直接毙命等等,都是大家可以发挥想象的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值