快速多人游戏(2) - 客户端预测和服务器校验

来源:互联网 发布:张子萱怀孕时淘宝照片 编辑:程序博客网 时间:2024/06/05 17:16

http://blog.csdn.net/silangquan/article/details/51512400

原文链接:Fast-Paced Multiplayer (Part II): Client-Side Prediction and Server Reconciliation

介绍

在第一篇文章中,我们介绍了一种权威服务器的C-S模型,在这种模型中,客户端只将输入发送到服务器中,当收到来自服务器的游戏状态更新的时候再将结果渲染出来。


单纯地依赖这种模型会导致玩家输入命令和画面更新之间的延迟感,比如,当玩家按下向右的按钮,然后玩家等了半秒钟才开始移动,因为首先客户端需要把输入传给服务器,然后服务器处理了出入之后计算出新的游戏状态,然后再将新的游戏状态传回给客户端。



网络延迟的效果


在实际的网络环境中,延迟可能会达到零点几秒,这时游戏就有点感觉延迟了,最坏的情况是直接没法玩了。在这篇文章中,我们将找到减小这种延迟感的方法,甚至能够消除这种延迟。


客户端预测

虽然存在着作弊的玩家,但是大部分时候服务器是处理有效访问的(来自非作弊玩家的),这也意味着服务器收到大部分的输入都是有效的,而且游戏状态的更新也是按照预期的,也即是说:如果你的角色在(10,10),然后方向键右被按下了,那么你的角色就会移动到(11,10)。


我们可以利用这一点,如果游戏世界的确定性足够(给定一个游戏状态和一些列的输入,得到的结果是完全可预测的)。


现在假设有100ms的延迟,角色移动一格需要花费100ms,使用之前说的实现方式,那么整个动作完成需要花费200ms:



网络延迟和动画


因为游戏世界是确定的,我们假设传送到服务器的输入都能够成功执行。在这种假设下,客户端能够预测游戏世界在接受输入后的状态,并且绝大部分情况下,结果是正确的。

与之前说的将客户端的输入发送到服务器然后等待服务器的响应再在客户端做出反应,我们可以将两者同时进行,即当在发送输入信息的时候,就当作它们已经正确执行,通常服务器返回的结果和客户端的执行的结果是一致的:



在服务器确认行为的同时执行动画


现在玩家输入和运行结果之间就没有任何延迟存在了,同时服务器还是权威服务器。(如果有作弊的客户端发送无用的消息,他可以把角色放在他想要的任何地方,但这并不会影响到服务器,也就是其他玩家也不会受到他的影响)。


同步的问题

在上面的例子里,我选用了一些比较特例的数字来让所有事情看起都很完美,现在假设另一种情况,假设现在服务器延时250ms,移动一格花费100ms,现在玩家按了两下方向键右,想要往右移动两格。


使用之前我们说的技术,情况如下图所示



客户端预测和服务器返回的状态不匹配


当 t = 250ms的时候,也就是收到服务器第一个包的时候,有趣的事发生了,客户端预测的状态是x=12,但是服务器说新的游戏状态是x=11,因为服务器是权威的,客户端必须把玩家拖回到x=11的位置,然后再t=350的时候,又来了一个新包,说角色应该在x=12,角色又移动了一次 。


从玩家的角度看,它按下了两次按键,角色向右移动了两次,然后过了50ms之后,又向左跳了一格,站了100ms之后,又向右跳了一格,这当然是没法接受的。


服务器调和

解决上面说的问题的办法就是你得意识到,客户端所看到的世界是当前的,但是但是因为延迟,客户端得到的游戏状态都是过去的,当服务器在发送更新的时候,它并没有把客户端发来的所有的命令都处理掉。


这就很麻烦了,首先,客户端在发送请求包的时候,给每个包打上帧序,在上面的例子里,第一个按键请求的编号是#1,第二个按键请求时#2,然后当服务器回复的时候,包的包含着请求的序列号。




客户端预测+服务器调和


当t = 250的时候,服务器说“基于#1的输入请求,你的位置是11”.因为服务器是权威的,所以角色的位置是x=11, 现在假设客户端保存了一份发送到服务器的请求,基于新的游戏状态,它知道服务器已经处理了请求 #1,所以他将这个备份丢弃掉,但是客户端知道服务器还需要将#2请求的回复发过来,所以再一次进行客户端预测,客户端可以基于上次服务器发来的状态和#2输入来计算当前的状态。


所以当t=250ms的时候,客户端得到“经过#1的输入之后,x=11”,它将#1输入的拷贝丢弃掉,但是还有一份并没有得到服务器返回的#2的拷贝,客户端将内部游戏状态更新为服务器所发来的游戏状态,也就是x=11,然后将所有未收到回复但已经发送给服务器的输入用于预测,在上面的例子,输入就是向右移动,最终状态就是x=12,这是正确的。当t=350的时候,一个新的游戏状态从服务器获得;这种情况它说“基于#2输入,x=12”。在这是,客户端将#2请求之前的请求都丢弃,然后将游戏状态更新为x=12,没有需要预测的输入,所以不需要预测,现在得到的结果都是正确的。


一些其他的问题

上面讨论的例子里只讨论了移动,但是这个方法在其他方面也可以使用。比如,在一个回合制的游戏里,当玩家攻击其他玩家的时候,你可以播放飙血的特效和伤害值,但是你不应该在收到服务器回复之前直接更新玩家的血量。


因为游戏状态的复杂性,它并不能保证是绝对可逆的,你也许希望只有再收到服务器确认的时候才将一个玩家杀死,即使这个玩家的血量降到负的了。(但假象一种情况,一个玩家实际上已经死了,但是在没有收到服务器确认之前他使用了药包,这个时候你该怎么处理?)


这又带来了一个有趣的问题,即使世界是绝对的确定的,并且没有玩家在作弊,还是有客户端的预测和服务器不吻合的情况。上面说的情况在单人游戏中不会出现,但是在多人接入同一个服务器的时候就会发生,这个问题会在下面的文章中进行讨论。


总结

当使用权威服务器的时候,你需要给玩家即时的响应,即使是当在等待服务器处理出入。为了达到这样的目的,客户端模拟出输入的结果,当收到服务器的回复时,客户端的游戏状态需要通过服务器的回复和还未处理的输入进行重新计算。


0 0
原创粉丝点击