原文:https://www.gabrielgambetta.com/entity-interpolation.html
在本文中,我们将探讨让其他玩家控制的角色连接到同一服务器的结果。
服务器时间节奏(帧同步)
在上一篇文章中,我们描述的服务器的行为非常简单——它读取客户端输入,更新游戏状态,并将其发送回客户端。但是,当连接多个客户端时,主服务器循环会有所不同。
在这种情况下,几个客户端可能同时以很快的速度发送输入操作(让玩家尽可能快地发出命令,无论是按箭头键、移动鼠标还是点击屏幕)。每次从每个客户端接收到输入后更新游戏世界,然后广播游戏状态会消耗太多的CPU和带宽。
一种更好的方法是在接收到客户端输入时对其进行排队,而不进行任何处理。相反,游戏世界会以低频率定期更新,例如每秒10次。每次更新之间的延迟(在本例中为100毫秒)称为时间步(帧同步的概念)。在每次更新循环迭代的时候,执行所有未处理的客户端输入(可能比时间步增量(帧同步的时间间隔)小,以使物理更可预测),并向客户端广播新的游戏状态。
总之,游戏世界以可预测的速度独立于客户端输入的存在和数量进行更新。
处理低频更新
从客户端的角度来看,这种方法和以前一样顺利地工作——客户机端预测独立于更新延迟工作,因此它显然也在可预测(如果相对不经常)的状态更新下工作。但是,由于游戏状态是以较低的频率广播的(继续这个例子,每100毫秒一次),客户端对于可能在世界各地移动的其他实体的信息非常稀少。
第一种实现是在接收到状态更新时会更新其他字符的位置;这会立即导致非常不稳定的移动,即每100毫秒进行一次离散跳跃,而不是平滑的移动。
根据你正在开发的游戏类型,有很多方法可以解决这个问题;一般来说,游戏实体的可预测性越高,就越容易找到正确的方法。
推算定位(客户端预位移其他玩家的移动)
假设你在玩赛车游戏。一辆速度非常快的汽车是可以预测的——例如,如果它以每秒100米的速度行驶,一秒钟后,它将比它开始行驶的地方提前大约100米。
为什么是大约呢?在那一秒内,汽车可能加速或减速了一点,或者向右或向左转了一点——这里的关键词是“一点”。汽车的机动性是这样的:在高速行驶时,无论玩家实际做什么,其在任何时间点的位置都高度依赖于其先前的位置、速度和方向。换句话说,赛车不能立即进行180度转弯。
对于每100毫秒发送一次更新的服务器,这是如何工作的?客户端接收到服务器发送来的每个其他车的速度和行驶方向;在接下来的100毫秒内,它不会接收到任何新信息,但仍需要显示它们的运行情况。要做的最简单的事情是假设汽车的航向和加速度在100毫秒内保持不变,并使用该参数在本地运行汽车物理。然后,100毫秒后,当服务器更新到达时,汽车的位置被修正。
根据许多因素,校正可以是大的或相对小的。如果玩家把车保持在直线上,并且不改变车速,那么预测的位置将与修正的位置完全相同。另一方面,如果玩家撞到什么东西,预测的位置将是非常错误的。
请注意,推算定位可以应用于低速情况,例如战列舰。实际上,“dead reckoning”一词起源于航海。
Entity 插值(客户端显示其他玩家之前的位置)
有些情况下,推算定位根本无法应用——特别是,所有情况下,玩家的方向和速度都会立即改变。例如,在3D射手中,玩家通常以非常高的速度跑、停、转弯,这使得推算定位基本上是无用的,因为位置和速度无法从以前的数据中预测出来。
你不能仅仅在服务器发送权威数据更新玩家的位置;你会让玩家每100毫秒一次短距离传送,使游戏无法玩。
你所拥有的是每100毫秒一次的权威位置数据;诀窍在于如何向玩家展示中间发生了什么。解决方案的关键是显示其他玩家之前与当前玩家相对的关系
假设您在t=1000时收到位置数据。你已经收到了t=900的数据,所以你知道玩家在t=900和t=1000的位置。所以,从t=1000和t=1100,你可以看到另一个玩家从t=900到t=1000的表现。这样,您总是显示用户的实际移动数据,除了显示100毫秒“延迟”。
你用来插入t=900到t=1000的位置数据取决于游戏。插值通常足够好用。如果没有达成你想要的效果,您可以让服务器在每次更新时发送更详细的移动数据,例如,一系列直线段发送给玩家,或者每隔10毫秒采样一次位置,插入时看起来更好(您不需要发送10倍以上的数据,因为您发送的是小移动的增量,可以针对这种特殊情况的线段的格式进行大量优化)。
请注意,使用这种技术,每个玩家看到的游戏世界呈现略有不同,因为每个玩家看到的是当前的自己,但看到的是过去的其他实体。然而,即使对于一个快节奏的游戏,看到其他实体100毫秒通常也不明显。
也有例外——当你需要大量的空间和时间精度时,比如当玩家向另一个玩家射击时。由于其他玩家在过去被看到,所以你的瞄准延迟了100毫秒——也就是说,你的目标是100毫秒前的地方!我们将在下一篇文章中讨论这个问题。
总结
在一个拥有权威服务器、不经常更新和网络延迟的客户机-服务器环境中,您仍然必须给玩家连续性和流畅移动的错觉。在本系列的第2部分中,我们探索了一种使用客户端预测和服务器协调来实时显示用户控制的玩家移动的方法;这确保了用户输入对本地玩家立即执行,消除了使游戏无法运行的延迟。
然而,其他玩家仍然是一个问题。在本文中,我们探讨了两种处理方法。
第一种是推算定位,适用于某些模拟,其中实体位置可以通过以前的实体数据(如位置、速度和加速度)进行可接受的估计。当这些条件不满足时,这种方法就失败了。
第二个Entity插值,不去预测未来的位置,它只使用服务器提供的真实实体数据,所以其他玩家的显示会稍有延迟。
这种网络效应就是当前玩家显示的是现在的位置,其他玩家显示的是过去的位置。这通常会创造一种难以置信的无缝体验。
然而,如果没有其他的事情发生,问题已经解决啦,但是当一个事件需要高的空间和时间准确性时,这种错觉就会消失,例如对移动目标进行射击:客户端2呈现在客户端1的位置与服务器和客户端1的位置不匹配,因此无法进行headshots !由于没有headshots 的游戏是不完整的,我们将在下一篇文章中处理这个问题。