最近是看到知乎上的关于网络游戏位置同步问题的讨论?整理了下资料,总结如下:
在实时网络游戏中,如何尽可能的做到玩家位置的同步?
可以参考一下文章:
http://www.zhihu.com/question/29076648
http://blog.codingnow.com/2006/04/sync.html
http://www.skywind.me/blog/archives/1145
比较好理解的一种方法就是影子跟随算法
影子跟随算法由普通DR(dead reckoning)算法发展而来,我将其称为“影子跟随”意再表示算法同步策略的主要思想:
1、屏幕上现实的实体(entity)只是不停的追逐它的“影子”(shadow)。
2、服务器向各客户端发送各个影子的状态改变(坐标,方向,速度,时间)。
3、各个客户端收到以后按照当前重新插值修正影子状态。
4、影子状态是跳变的,但实体追赶影子是连续的,故整个过程是平滑的。
同样解决该问题的算法貌似有:
帧间同步:不同客户端每帧显示相同的内容,键盘/时钟数据传到服务器,服务器确认后所有终端做出响应,多用于局域网游戏,比如红警(需要等待客户端),街霸II的网络版(360),可参考 LockStep,TimeWrap算法,网速要求高,复杂度低。这也是我们通常一般能够想到的方法。
假设你(玩家A)与服务器之间存在100ms的延迟(单向,往返则是200ms),其他玩家的延迟忽略不计,服务器的通信频率足够大(频率不够大还会造成其他很严重的问题,这个放在后面讲)。你在某一刻向服务器发送了一个请求(比如向前走),那么这个请求会在100ms之后到达服务器。服务器判定后返回结果,再经过100ms你的客户端会收到确认,服务器已经把你的位置向前移动了若干距离。假设客户端在没有收到任何服务器的更新前画面都不会变化,那么着200ms内你就会觉得游戏“卡顿”。
实际上在很多游戏里(比如CSGO)在这200ms里你看到你自己是在向前走,实际上那只是客户端“擅自”在绘制你前进的样子。这是一种延迟补偿策略,称为“客户端预测法”。也就是说客户端能够大致预测游戏未来的走向,因此在接收到服务器更新前会把预测到的画面先绘制出来(比如移动,武器的开火效果,弹药计数的变化等等)。收到服务器通信后如果有出入则立刻纠正为服务器提供的数据。这也是很多时候如果延迟很大玩家会体验到“明明往前走了过了一会又瞬移回到之前的位置”的原因。类似的体验还有“明明开了好几枪而且也都显示了过了一会弹药计数只减少了一点点”。
既然扯到这里了那就认真的讲下延迟补偿策略(lagcompensation)。一般补偿策略分服务器端和客户端两类。前面的预测法属于客户端一侧的。其他的客户端一侧的策略还有插帧法。所谓插帧法就是客户端会记录之前一次从服务器收到的信息,然后在接受到下一次通信的时候不立刻更新游戏画面,而是逐渐的更新画面(比如两次通信间玩家B移动了10单位距离,客户端会绘制玩家B以一定的速度移动了这10单位距离,而非立刻绘制玩家B瞬间移动了10单位距离)。插帧法的问题在于如果玩家并未沿直线运动且其直线路径中有本应不能通过的物体在(比如绕过一堵墙),客户端会绘制出该玩家穿墙而非绕过去的动作。
插值同步:不同客户端显示不同步,但是状态同步,常见的Dead Reckoning(或叫导航插值),效果好,但复杂度高。常见于竞速类游戏和 FPS游戏。
关于位置同步云风的观点:
首先谈谈位置同步。
好的位置同步一定要考虑网络延迟的影响,所以,简单把 entity 的坐标广播到 clients 不是一个好的方案。我们应该同步的是一个运动矢量以及时间信息。既,无论是 client 还是 server ,发出和收到的信息都应该是每个 entity 在某个时刻的位置和运动方向。这样,接收方可以根据收到的时刻,估算出 entity 的真实位置。对于 server 一方的处理,只要要求 client 按一个频率(一般来说战斗时 10Hz 即可,而非战斗状态或 player 不改变运动状态时可以更低) 给它发送位置信息。server 可以在网络状态不好的情况下依据最近收到的包估算出现在 player 位置。而 client 发出的每次 player 位置信息,都应该被 server 信任,用来去修正上次的估算值。而 server 要做的只是抽查,或交给另一个模块去校验数据包的合法性(防止作弊)。
在 server 端,每个 entity 的位置按 10Hz 的频率做离散运动即可。
client 因为涉及显示问题,玩家希望看到的是 entity 的连续运动,所以处理起来麻烦一点。server 发过来的位置同步信息也可能因为网络延迟晚收到。client 同样根据最近收到的包做估算,但是再收到的包和之前已经收到的信息估算结果不同的时候,应该做的是运动方向和速度的修正,尽可能的让下次的估算更准确。
关于战斗指令同步,我希望是给所有战斗指令都加上冷却时间和引导时间,这正是 wow 的设计。这样,信任 client 的时间戳,就可以得到 client 准确的指令下达时间。引导时间(或者是公共冷却时间)可以充当网络延迟时间的缓冲。当然我们现在的设计会更复杂一些,这里不再列出。对于距离敏感的技能,例如远程攻击和范围魔法,我们的设计是有一个模糊的 miss 判定公式,解决距离边界的判定问题。
这里, server 对攻击目标的位置做估算的时候,可以不按上次发出包的运动方向去做位置估计,而选择用最有利于被攻击者的运动方向来做。这样,可以减少网络状况差的玩家的劣势。
对于 PVE 的战斗,甚至可以做更多的取舍,达到游戏流畅的效果。比如一个网络状态差的玩家去打 npc,他攻击 npc 的时刻,npc 是处于攻击范围之内的。但是由于网络延迟,数据包被 server 收到的时候,npc 已经离开。这个时候 server 可以以 client 的逻辑来将 npc 拉会原来的坐标。
虽然,这样做,可能会引起其他玩家(旁观者)client 上表现的不同。但是,网络游戏很多情况下是不需要严格同步的。在不影响主要游戏逻辑的情况下,player 的手感更为重要。