游戏网络物理碰撞

50
如何在典型的客户端/服务器设置中模拟两个由客户端控制的车辆发生(明智的)碰撞?我确实阅读了关于如何在分布式网络物理方面进行处理(不使用传统的客户端预测)的这篇杰出博客文章,但是这个问题特别涉及如何处理拥有对象的碰撞。

示例

假设客户A领先服务器20毫秒,客户B领先服务器300毫秒(计算延迟和最大抖动),这意味着当两个车辆发生碰撞时,两个客户都会看到对方在另一个方向上相对于另一个车辆的速度落后320毫秒。在瑞典公路上迎面而来意味着差异16米/17.5码!

不要尝试的方法

由于我的车辆非常复杂,具有各种关节和身体,这些身体又具有线性和角位移、速度和加速度,更不用说用户输入的状态了,因此几乎不可能外推位置。


1
玩一些马里奥卡世界联机,你就可以感受到延迟对其物理/碰撞的影响。 - FogleBird
8个回答

12
我不知道是否存在完美的解决方案,而且我有一种感觉,即这样的解决方案不存在。即使您可以准确预测车辆的未来位置,您也无法预测用户操作控件的方式。因此,问题在于最小化客户端/服务器延迟的负面影响。考虑到这一点,我会从最小惊讶原则(摘自维基百科)的角度来处理:

在用户界面设计中,最小惊讶原则(或惊奇原则)指当界面的两个元素发生冲突或模糊时,行为应该是最不会让人类用户在冲突发生时感到惊奇的行为。

在您的示例中,每个用户都看到两辆车。他们自己的车和另一个玩家的车。用户期望自己的车辆表现出他们所控制的方式,因此我们无法改变模拟的这个方面。然而,用户无法确切知道其他用户如何控制他们的车辆,我会利用这种模糊性来隐藏用户的延迟。

以下是基本思路:

  1. 服务器必须决定即将发生的碰撞。碰撞检测算法不需要完美到100%,只需要足够接近,以避免明显的不一致。
  2. 一旦服务器确定两个车辆将相撞,它会向这两个用户发送消息,指示即将发生碰撞。
  3. 在客户端A上,车辆B的位置被调整(真实地)以保证发生碰撞。
  4. 在客户端B上,车辆A的位置被调整(真实地)以保证发生碰撞。
  5. 在碰撞后,如有必要,可以调整每辆车的位置,以便最终结果与游戏的其他部分保持一致。这部分正是MedicineMan在他的回答中提出的。

这样,每个用户仍然完全控制自己的车辆。当发生碰撞时,它不会是意料之外的。每个用户都会看到另一辆车向他们移动,他们仍然会感到实时模拟的感觉。好处是,这种方法在低延迟条件下反应良好。如果两个客户端与服务器的连接具有低延迟,则调整量将很小。当然,随着延迟的增加,最终结果会变得越来越差,但这是不可避免的。如果有人在连接具有数秒延迟的情况下玩快节奏动作游戏,他们简单地无法获得完整的体验。


1
没错!你把事故后的计算隐藏起来的想法非常好。第五步应该献给你 :) 我建议的关键是在碰撞发生之前对另一辆车的表面位置进行微小的调整。我希望我在回答中已经清楚地表明了这个区别。 - e.James
步骤1的一个问题是服务器将不得不推断比当前服务器时间提前600毫秒以获取客户端B的位置。至少,在快节奏的赛车游戏中,0.6秒是非常长的时间。 - Jonas Byström
此外,当你考虑到第三辆车撞击你的两辆车(但在玩家屏幕上的不同位置)时,这种情况开始感觉奇怪。 - Blindy
2
这种方法可能适用于直接的碰撞,但过于简单了,因为大多数碰撞只是轻微的相互弹跳。例如考虑一辆卡车在拖车上放着吉普车,或者两辆赛车并排行驶。服务器模拟会很快分歧。 - Jonas Byström
@JonasByström,为什么是0.6秒而不是0.3秒? - Prof. Falken
@AmigableClarkKant:当然要看解决方案,但对于300毫秒客户端的往返时间,可以使用2x.3秒或1x.6秒的外推。我选择的解决方案使所有客户端的延迟和抖动都超前于服务器,因此不需要外推,因为在具有高级物理模拟的游戏中这是不可行的。 - Jonas Byström

6
也许最好的方法不是实时显示碰撞,而是制造一种事情正在实时发生的假象。由于客户端落后于服务器(延迟),服务器需要显示碰撞的结果。因此,客户端可以显示闪光、爆炸或其他图形来分散用户注意力,为服务器端计算碰撞结果赢得足够的时间。当预测完成后,将其返回客户端进行展示。请注意保留HTML标记。

这个解决方案不是太幼稚了吗?如果在服务器上从未发生碰撞怎么办? - Jonas Byström
1
投票支持这个酷点子。也许可以通过假装从未发生过的预测碰撞实际上是不严重到足以造成任何类型反馈给相互碰撞的物体的碰撞来解决问题。 - Ross

2
关于“不要尝试”的问题。您认为需要做出完美的预测,但在具有复杂物理特性的游戏中,您永远无法找到完美的解决方案。以近似值为最佳解(例如,大多数商业物理引擎可以将一个形状投射到物理场景中并返回第一个碰撞点)。
例如,我在Glenn(您提到的博客作者)的指导下,实现了《雇佣兵2》的网络物理的一些关键部分。即使对于单个刚体,也不可能将所有必要的物理状态全部传输到网络上。Havok物理每帧逐渐生成接触点,因此当前的“接触图”是保持模拟确定性的必要部分。这也是太多的数据。相反,我们发送所需的变换和速度,并使用力和扭矩轻轻地将物体推入正确位置。错误是不可避免的,因此您需要一个好的错误校正方案。

1
一个更具体的建议(已经被提出,但值得重申):保留一个网络相关物体的备用物理场景,使用简化形状和车辆物理模型进行模拟。从远程玩家的最后已知帧到当前本地帧进行模拟,并报告任何高速碰撞的近似点。这将允许一种便宜而“相当不错”的预测,可以与误差校正技术相结合。 - Evan Rogers
我最终做的是尽可能地模拟物理实体与远程端接近,但“推动”(或者更确切地说是“滑动”)图形表示。类似的解决方案,但我认为这样可以获得更好的确定性。 - Jonas Byström

2

很抱歉我的回答是“不要尝试”,但我从未听说过不涉及客户端预测结果的解决方案。考虑一个简化的例子:

客户端A静止不动,观察客户端B的车辆接近悬崖。客户端B的车辆能够在最后一刻立即减速到0,并在掉下悬崖之前做到这一点。

如果客户端A试图实时显示客户端B的状态,则客户端A别无选择,只能预测客户端B已经掉下悬崖。在许多设计为玩家角色能够在全速奔跑时立即停止的MMORPG中,您会看到这种情况。否则,客户端A可以随着状态更新而显示客户端B的状态,但在您的情况下,这是不可行的,因为客户端A需要能够与客户端B实时交互(我猜测)。

您是否可以尝试简化碰撞模型,以便进行实时预测?也许让您的“关节和身体”具有处理器不密集的物理模型,例如几个立方体或球体。我不太熟悉如何提高碰撞检测的效率,但我认为可以通过检测比视觉模型更简单的模型的碰撞来完成。


好主意,但不幸的是很难实现。我的所有模型已经达到最小化了,例如挖掘机只有三个关节,分别是臂、臂杆和铲斗。 - Jonas Byström

1
我最终所做的是完全跳过预测,仅仅是这样做:
  1. 客户端对自己的位置有很大的控制权,
  2. 服务器(几乎)只在与其他动态对象(即非静态环境)发生“高能”碰撞时说一些关于拥有客户端位置的事情。
  3. 当从服务器接收到一个位置更新时,客户端需要采取 meshoffset=meshpos-physpos,然后每帧设置 meshpos=physpos+meshoffset,并逐渐减少 meshoffset
在大多数情况下看起来相当不错(在低延迟的情况下),我甚至不必使用 Slerp 来获得平滑的过渡。
跳过预测可能会给高延迟的客户端带来可怕的体验,但如果我要发布这个独立游戏,我就没有时间去纠结这个问题。偶尔创建一个半吊子的解决方案,能够工作得足够好,但不是最好的,也是很好的选择。;)

编辑:最终我还是加入了Glen Fiedler(问题中提到的博主)在《战地2》中实现的“所有权”功能:每个客户端会暂时获得它们碰撞到的(动态)对象的所有权。这是必要的,因为否则在高延迟和高速情况下穿透会变得很深。当你看到GDC视频演示时,这种解决方案的效果就像你想象的那样好,我绝对推荐它!


1
那么,客户端可以告诉服务器关于它的位置的任何想法?我不看到这可能会被滥用。 - BlueRaja - Danny Pflughoeft
1
添加一个松弛约束条件,检查客户端偏差是否过大,需要五分钟的编码时间。这可能足够长时间使用了。 - Jonas Byström

1

一些想法。

  1. 点对点更擅长处理延迟和高速。

因此,如果这是您自己的引擎,则切换到点对点。然后,根据其他对等方的按钮输入向前移动,将其车辆进行外推,以确定其当前位置。您可以设置碰撞,使其与其他车辆发生碰撞,就像它是世界上的东西一样。也就是说,您承受了打击。

这意味着当您与其他人相撞时,您会弹开,在对等方的网络上,他们会弹开,因此看起来大致正确。延迟越低,它的工作效果越好。

  1. 如果要进行客户端/服务器,则这将劣于p2p

尝试以下事项 o)像p2p一样向前推算客户端以执行碰撞检测。 o)将碰撞结果发送回客户端并向前外推。

请注意,这永远不可能像p2p那样好。从根本上讲,高速和延迟=误差,因此消除延迟是最佳策略。P2P做到了这一点。


点对点交易虽然方便,但存在与欺诈者打交道的风险。 - Macoy

0
除了在客户端预测其他用户可能出现的位置并将碰撞信息及其处理方式发送到服务器之外,大多数 MMO 处理延迟的方法是让服务器“向过去”运行。基本上,他们缓冲最近的输入,但只对发生在过去 0.1 秒内的事件做出反应。这样,当您需要时(即当碰撞即将在您的时间范围内发生时),您可以查看缓冲的输入以查看会发生什么,并决定碰撞是否真实。
当然,这为您的程序增加了额外的复杂性,因为您必须考虑要向客户端发送哪些数据以及他们应该如何对其做出反应。例如,您可以将整个“未来”缓冲区发送到客户端,让他们看到哪些可能的碰撞实际上会发生,哪些不会发生。

这会使服务器比客户端更滞后运行。或者你的意思是客户端和服务器都在“过去”运行,以便正常的管道可以被更为当前但不准确的“近似管道”所覆盖吗? - Jonas Byström
所有的事情都是“过去式”,但是带有一些即将发生的事件的信息。它真的不必太远,当然不足以真正注意到,只需要足够给你一个统一的缓冲区来帮助预测碰撞(和其他事情)。 - Blindy

-1
罗斯有一个好点。 你可以简化用于检测碰撞的模型,通过将其抽象为一些更简单的容积(即车辆的大致盒状轮廓)。 然后,您可以基于简单的体积进行预测,并在用户分心“爆炸”时对精细计算进行详细计算。 这可能不是完美的,但可以加快碰撞检测速度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接