【转载】快节奏多人游戏(第 III 部分):实体插值

[介绍]

在本系列的[第一篇文章]( 【转载】快节奏多人游戏(第一部分):客户端-服务器游戏架构 - Creator 3.x - Cocos中文社区)中,我们介绍了 权威服务器 的概念及其在防止客户端作弊方面的有用性。但是,天真地使用这种技术可能会导致有关可玩性和响应能力的潜在问题。在[第二篇文章]( 【转载】快节奏多人游戏(第 II 部分):客户端预测和服务器对帐 - Creator 3.x - Cocos中文社区)中,我们提出了 客户端预测 作为克服这些限制的一种方式。

这两篇文章的最终结果是一组概念和技术,这些概念和技术允许玩家以一种感觉与单人游戏完全一样的方式控制游戏中的角色,即使通过具有传输延迟的 Internet 连接连接到权威服务器也是如此。

在本文中,我们将探讨将其他玩家控制的角色连接到同一服务器的后果。

[服务器时间步长]

在上一篇文章中,我们描述的服务器的行为非常简单 – 它读取客户端输入,更新游戏状态,并将其发送回客户端。但是,当连接多个客户端时,主服务器循环会有所不同。

在这种情况下,多个客户端可能会同时以快速的速度发送输入(只要玩家可以发出命令,无论是按箭头键、移动鼠标还是单击屏幕)。每次从每个客户端接收到输入时更新游戏世界,然后广播游戏状态会消耗过多的 CPU 和带宽。

更好的方法是在收到客户端输入时对其进行排队,而不进行任何处理。相反,游戏世界会以低频率定期更新,例如每秒 10 次。每次更新之间的延迟(在本例中为 100 毫秒)称为 时间步 长。在每次更新循环迭代中,都会应用所有未处理的客户端输入(可能以比时间步长更小的时间增量,以使物理特性更具可预测性),并将新的游戏状态广播到客户端。

总之,游戏世界的更新速度与客户端输入的存在和数量无关。

[处理低频更新]

从客户端的角度来看,这种方法和以前一样顺利——客户端预测独立于更新延迟工作,因此它显然也可以在可预测的(如果相对不频繁)状态更新下工作。但是,由于游戏状态以低频率广播(继续本示例,每 100 毫秒一次),因此客户端拥有有关可能在世界中移动的其他实体的信息非常稀疏。

第一个实现将在收到 state update 时更新其他字符的位置;这立即导致非常不稳定的运动,即每 100 毫秒出现一次离散的跳跃,而不是平滑的运动。


客户端 2 看到的客户端 1。

根据你正在开发的游戏类型,有很多方法可以解决这个问题;通常,游戏实体的可预测性越高,就越容易正确处理。

[航位推算]

假设您正在制作一款赛车游戏。一辆行驶速度非常快的汽车是可以预测的——例如,如果它以每秒 100 米的速度行驶,那么一秒后它将比起点领先大约 100 米。

为什么是 “大致”?在那一秒钟内,汽车可能会加速或减速一点,或者向右或向左转一点——这里的关键词是“一点”。汽车的机动性是这样的,在高速下,它在任何时间点的位置都高度依赖于它之前的位置、速度和方向,而不管玩家实际做什么。换句话说,赛车不能立即进行 180º 转弯。

对于每 100 毫秒发送一次更新的服务器,这是如何工作的?客户获得权威的速度并前往每辆竞争汽车;在接下来的 100 毫秒内,它不会收到任何新信息,但仍需要显示它们正在运行。最简单的方法是假设汽车的航向和加速度在这 100 毫秒内保持不变,并使用这些参数在本地运行汽车物理。然后,100 毫秒后,当服务器更新到来时,汽车的位置被纠正。

修正可能很大或相对较小,具体取决于很多因素。如果玩家确实将汽车保持在一条直线上并且不改变汽车速度,则预测的位置将与校正后的位置完全相同。另一方面,如果玩家撞到某物,则预测的位置将非常错误。

请注意,航位推算可以应用于低速情况 - 例如战列舰。事实上,“航位推算”一词起源于海上航行。

[实体插值]

在某些情况下,根本无法应用航位推算——特别是玩家的方向和速度可以立即改变的所有情况。例如,在 3D 射击游戏中,玩家通常以非常高的速度奔跑、停止和转弯,这使得航位推算基本上毫无用处,因为无法再从以前的数据中预测位置和速度。

您不能只在服务器发送权威数据时更新玩家位置;你会遇到每 100 毫秒传送一次短距离的玩家,使游戏无法玩。

您拥有的是每 100 毫秒的权威位置数据;诀窍是如何向玩家展示两者之间发生的事情。解决方案的关键是显示 过去 其他玩家相对于用户的玩家。

假设您在 t = 1000 时收到位置数据。您已经在 t = 900 时收到了数据,因此您知道玩家在 t = 900t = 1000 时的位置。因此,从 t = 1000t = 1100 ,您展示了从 t = 900t = 1000 的其他玩家做了什么。这样,您始终向用户显示 实际移动数据 ,但您显示它 “延迟” 100 毫秒。


客户端 2 呈现客户端 1 “过去”,插入最后已知的位置。

您用来从 t = 900t = 1000 插值的位置数据取决于游戏。插值通常效果很好。如果没有,您可以让服务器在每次更新时发送更详细的移动数据 - 例如,玩家跟随的一系列直线段,或者每 10 毫秒采样一次的位置,这在插值时看起来更好(你不需要发送 10 倍的数据 - 因为你为小的移动发送增量,所以可以针对这种特殊情况对 wire 上的格式进行大量优化)。

请注意,使用此技术时,每个玩家看到的游戏世界呈现略有不同,因为每个玩家看到 的是现在 的自己,但看到 的是过去的 其他实体。但是,即使对于快节奏的游戏,看到 100 毫秒的其他实体通常也不会明显。

也有例外 – 当您需要大量的空间和时间精度时,例如当玩家向其他玩家射击时。由于其他玩家是过去的,因此您的瞄准延迟为 100 毫秒 – 也就是说,您正在 100 毫秒前的目标位置射击!我们将在下一篇文章中讨论这个问题。

[总结]

在具有权威服务器、不频繁更新和网络延迟的客户端-服务器环境中,您仍然必须给玩家连续和流畅移动的错觉。[在本系列的第 2 部分中]( 【转载】快节奏多人游戏(第 II 部分):客户端预测和服务器对帐 - Creator 3.x - Cocos中文社区),我们探索了一种使用客户端预测和服务器对账实时显示用户控制的玩家移动的方法;这可确保用户输入对本地玩家产生直接影响,从而消除导致游戏无法播放的延迟。

但是,其他实体仍然是一个问题。在本文中,我们探讨了处理它们的两种方法。

第一种是 航位推算 ,适用于某些类型的模拟,其中实体位置可以根据以前的实体数据(如位置、速度和加速度)进行可接受的估计。如果不满足这些条件,此方法将失败。

第二个 ( entity interpolation) 根本不预测未来位置 – 它只使用服务器提供的真实实体数据,从而显示其他实体的时间略有延迟。

净效果是,用户的玩家在 现在 看到,而其他实体则 在过去 看到。这通常会创造令人难以置信的无缝体验。

但是,如果不做任何其他事情,当事件需要高空间和时间精度时,例如向移动目标射击,错觉就会崩溃:客户端 2 渲染客户端 1 的位置与服务器和客户端 1 的位置不匹配,因此爆头变得不可能!由于没有爆头的游戏是不完整的,因此我们将在下一篇文章中讨论这个问题。