在这个系列的第一篇文章中,我们介绍了权威服务器和其在防客户端作弊方面的用处。然而考虑到可玩性和反馈机制简单的应用这种技术也会带来相当大的问题。在第二篇文章中,我们提出了用客户端预演的方式来规避这些问题的方法。

在这两篇文章中的整套概念和技术只是允许玩家像在单机游戏中控制角色那样来操作,尽管是在一个存在网络连接延迟的连接了权威服务器的情况下。

在这篇文章中,我们将探索当有其他玩家连接在同一个服务器时的结果。

服务器的时间步

前文中,我们所描述的服务器行为非常简单——读取客户端输入信息,更新游戏状态,回传客户端。当多客户端连接时,主服务器的循环就会产生些许的不同了。

这种情景下,多个客户的可能同时发送输入信息,并且是以一种快节奏的形式。(同玩家发出指令的速度一样快,可能是按下方向键,移动鼠标或点击屏幕)。实时更新每个客户端发送的输入指令并广播其游戏状态将会消耗太多cpu和带宽。

更好的处理方式是将客户端的输入指令安卓其接收先后顺序进行排列,先不处理。而游戏世界则以一种低频的方式周期性的进行同步,例如:每秒10次。在这种情况下,每次更新的延迟时间为100ms,这就是所谓的服务器时间步。在每一次更新循环迭代中,所有没处理的客户端输入指令都已应用(可能在一个小于时间步的增量时间内,来保证物理上更可预测),同时新的游戏状态会广播至这些客户端。

总之,游戏世界的更新是独立于存在的客户端输入指令以外的,以一种可预估的方式。

处理低频更新

从客户端视角来看,这种方式和之前一样平滑——客户端预演独立于更新延迟以外,所以很明显也是在预期中进行的,只要状态更新相对较少。然而,由于游戏状态是以一种低频的方式进行广播的(继续之前的例子,每100ms),客户端只得到很少关于其他可能穿行于这个世界的其他实例的信息。

首先实现的是在收到游戏状态广播后同步其他角色的位置,这马上带来剧烈的运动,那就是各个单位每100ms跳一次位置,而不是平滑的移动。

 客户端2眼中的客户端1

根据你所开发的游戏类型不同,有很多的方法来处理这个问题;通常来说,你的游戏越具备可预测性,就越容易处理。

航位推测法

假设你在制作一款×××游戏,一辆×××的行进就非常好预测——比如,它以每秒100米的速度行驶,那么1秒后他就会出现在前方大概100米处。

为什么是“大概”?这一秒钟中,车辆可能加速或减速了一点,或向左向右转向了一点——关键词是“一点”。不考虑玩家真实操作,高速情况下其所处位置的可操作性取决于其当前位置、速度和方向,换言之,×××不会出现马上掉头的情况。

那么在一个服务器更新间隔为100ms的环境下,它将如何运作呢?客户端接收到每辆×××的权威速度和行进方向,在接下来的100ms中,客户端不会接收到任何新信息,但仍然需要显示他们的行驶状态。最简单的情况就是客户端假设在接下来的100ms中车辆的行进方向和加速度会保持不变,并在这种参数下本地去运行车辆的物理系统。接着,100ms之后当服务器的更新消息到达时,正车辆的位置信息得以修正。

这种修正可能很大也可能在众多依赖因素中相对较小。如果玩家确实保持车辆沿直线匀速行驶,那么预演位置将几乎与修正位置一致。另一种情况,如果玩家撞到了什么东西,那么预演位置则将大错特错。

注意,航位推测法可以在慢节奏的环境中应用——如战舰。事实上,“航位推测法”这个词组就是来源于海军术语的。

实例插值

某些环境下航位推测法完全无法应用,最突出的例子就是,所有情境下玩家的方向和速度都是实时可变的。再比如,一个3d射手,玩家经常使用跑步,停止和高速转向,这使得航位推测法基本失效了,因为位置和速度信息无法从先前的数据中推测出来。

你不能只在权威服务器下发时更新玩家位置信息,否则你将看到一个每100ms进行一次短距离传送的玩家,这就毁了游戏体验。

你拥有的只有每100ms得到的权威位置数据,而策略是如何表现角色在这期间的行为。解决这个问题的关键点在于相对于当前玩家的角色来说,去显示其他玩家的过去形态。

也就是说,你在第1秒钟时所接收到的位置信息,其实你已经在第900ms时收到了,因此你知道角色在第900ms和1秒钟时都处于什么位置。那么从第1秒到第1100ms,你的游戏显示了从第900ms到第1秒时其他角色都做了什么。这种方法你可以一直显示角色的实际行为数据,而不是在100ms之后再显示它。

 客户端2呈现出“过去的”客户端1,用最后获得的位置信息进行插值

从900ms到1秒时你所使用的插值位置信息取决于不同游戏。通常插值法工作相当良好。如果不行,那么你可以通过提高服务器每次发送的细节行为信息——例如,接下来玩家成序列的连续片段,或者每10ms进行一次位置采样,这样进行插值后的效果将更好(不用再发送10次更多其他信息,因为你将发送很多细微的位移变化,在这种特定情况下线上的效果将被极大的优化。)

注意,使用这种技术,每个玩家看到的游戏世界都将呈现出细微的差别,因为每个玩家会看到现时的自己和过去的其他实例。即使对于快节奏游戏来说,看到100ms前的其他实例这件事也不是轻易能够察觉的。

也有例外——当你需要大量立体的和临时性的精确信息,比如玩家射击击中其他玩家。由于所见的其他玩家是过去时,你的瞄准会有100ms的延迟——也就是说,你所击中的位置是你目标100ms前所处的位置!我们将在下一篇文章中处理这个问题。

小结:

在有权威服务器的客户端——服务器架构中,低频率的更新和网络延迟情况下,你必须给玩家一种连续的平滑运动的假象。在本系列第二篇文章中,我们探究了一种使用客户端预演和服务器调节来显示玩家实时控制角色行为的方法;这保证了用户的操作能够在本地角色上体现出即时效果,规避了延迟对游戏表现带来的低可玩性问题。

但无论如何其他实例仍然是个问题。这篇文章中,我们探究了2种解决方案。

第一个,航位推测法,应用在定情况下模拟实例位置,前提是其位置、速度和加速度情况的可预知性在可接受的范围内。当条件达不到上述情况时,这种方案将无法生效。

第二个,实例插值法,完全不去预测未来的位置——只使用由服务器提供的真实的实例信息,从而呈现稍微延迟过的其他实例。

其净效果是用户看到自己的现时,而看到其他实例的过去。这通常可以制造一种天衣无缝的体验。

然而,仍然没有得到解决的是,这种假象在高度立体和临时性的精确事件面前崩塌了,比如射击一个移动目标,在客户端2上呈现出的客户端1的位置和服务器以及客户端1上的位置都不匹配,这就导致了“爆头”成为了不可能!由于没有几个游戏完全没有“爆头”(意旨类似于爆头的关键性事件),我们将在下一篇文章中处理这个问题。