【问题标题】:Calculating collisions in real time - dealing with a delay in time实时计算碰撞 - 处理时间延迟
【发布时间】:2011-12-19 23:50:39
【问题描述】:

假设:

  • 您正在编写一个程序,其中 3 个(或更多)圆(或其他几何图形)在屏幕上实时移动,所有这些圆都具有不同的速度,由于物理计算,在特定时间可能会发生变化.

  • 只能对每一帧进行计算

  • 每一帧,你必须确保“碰撞”/“在这一帧和最后一帧之间的时间内发生碰撞”的圆圈会通过使用物理计算“反弹”

  • 假设在第 x 帧和第 x+1 帧之间的时间里,三个圆会相互碰撞。然而,在第 x 帧期间,没有一个圆圈与另一个圆圈接触。在第 x+1 帧中,同样的事情适用(没有碰撞) 我将尝试用一张图片更好地说明这一点:

问题:

有什么好的方法可以跟踪这样的碰撞,以免由于两帧之间的一些(意外)大的时间延迟而跳过碰撞?

这个问题在我脑海里萦绕了太久......

编辑:

致所有认为这篇文章过时的人:请在投票结束之前查看this

【问题讨论】:

标签: math real-time physics


【解决方案1】:

要正确执行此操作:

  • 计算准确圆的碰撞时间,而不是四舍五入到下一帧:
    • 如果您的对象以超过一维的方式移动,则需要寻根。
  • 按如下顺序解决冲突:
    • 在上述确切的碰撞时间停止模拟。
    • 解决第一次碰撞中圆的物理问题。
    • 重新计算以确定由新轨迹引起的任何碰撞。
    • 重新启动模拟。
    • 重复直到到达帧结束而不会发生冲突。
  • 任何其他计划外的物理变化也需要更新即将到来的碰撞。

您可能会注意到这有可能进行大量计算。如果你的球失去动能并最终相互依靠,这可能会特别令人讨厌——如果你的物理学使这种情况成为可能,你将需要为“静止接触”添加某种阈值(不幸的是,这会使你的物理学非常好)。


更新,以回应 cmets:我想明确表示我的回答忽略了您的一个假设 - 如果您假装帧边界之间没有任何时间,您将无法准确处理冲突。碰撞不会发生在帧边界;一般来说,冲突会发生在帧边界之间,因此您的计算需要反映这一点。

如果您假设帧之间的所有运动都是线性的(即,您的模拟在帧边界上进行所有加速),那么确定是否、何处以及何时发生碰撞实际上非常简单。它将您的帧间“模拟”减少到几乎没有——即使您的模拟是 2D 或 3D,您也可以以封闭形式求解方程:

posAB = posA - posB                [relative vector between circles A and B]
velAB = velA - velB                [relative velocity between circles A and B]
posAB(t) = posAB(0) + t * velAB    [relative vector as a function of time]
rAB = rA + rB                      [sum of radii of the two circles]
collision happens when distance(t) = abs(posAB(t)) == rAB
-> rAB^2 = | posAB(t) |^2 = | posAB(0) + t * velAB |^2
-> t^2 * |velAB|^2 + t * 2*posAB(0).velAB + |posAB(0)|^2 - rAB^2 == 0
solve quadratic equation for t:
   - if discriminant is negative, there is no collision.
   - if collision times are outside current timestep, there is no current collision.
   - otherwise, smallest t should be the correct collision time.
   - watch out for cases like 2 circles coming *out* of collision...

最后,听起来您正在尝试进行过早优化。最好让事情正常进行,然后让它们快速运行;在运行代码之前,您不会知道实际的瓶颈。事实上,我还认为你低估了现代计算机的力量。当然,你总是可以添加足够多的对象来阻塞你的计算机,但我想你会惊讶于它需要多少......

【讨论】:

  • 理论上听起来很美,但问题是:你什么时候计算碰撞?在 frameX 中,还是在 frameX+1 中?在 frameX 的情况下,你怎么知道你必须计算多长时间?您可以通过使用最后一次 time_elapsed 的时间加上一些偏移量来估计这一点,但是您无法确定吗?在 frameX+1 的情况下,您如何知道是否需要测试碰撞?这些对象可能完全相互传递,因此您不知道是否进行测试。我想你可以检查一切,但这听起来几乎是不可能的计算。
  • 尤其是当您有很多需要检查的对象时。要么,要么我严重低估了现代计算机系统的局限性。 (顺便说一句:对于 frameX,我的意思是在碰撞发生之前进行检查,对于 frameX+1,我的意思是在碰撞发生之后进行检查 [通过使用存储在先前帧中的变量])
  • 您可以进行保守的碰撞检测,方法是使用围绕您的对象在当前帧内可以移动的任何位置的球体或框。然后,使用加速结构,您可以有效地消除大多数可能的碰撞,并且只需要检查它们可能实际发生的确切碰撞。
  • 但这在理论上似乎是不可能的。因为[1]你无法确定距离下一帧还有多少时间,并且[2]理论上你的对象可以在任何地方移动,因为它的当前速度可以通过施加力的碰撞来改变,或者外力等(都在一帧中!)您知道有关此的任何良好信息来源吗?
  • [2]:当一个物体发生碰撞时,您应该为框架的剩余部分创建一个新的边界框,并查询您的加速结构以查看是否有任何新的潜在碰撞。如果您在帧内有加速度(而不是仅在帧边界,如我的更新中所述),您应该能够找到该加速度的上限,并使用它来确定您的保守边界框。
【解决方案2】:

仅在“框架”中评估事物的想法可能是不正确的。

最古老的 OO 模式之一是“Model-View-Controller” - 它鼓励 解耦 what 正在查看(模型)与 how 被查看(视图)。

物理引擎在模型对象上运行,因此会一直跟踪它们之间的交互 - 因此,当视图出现并询问“框架”中每个圆圈的位置和方向时x+1" 场景,它可以根据自上次查看请求以来经过的实时时间轻松回答。

【讨论】:

    【解决方案3】:

    我知道您的问题表明物理只能在每一帧进行评估,但我不明白什么时候会出现这种情况。以足够小的步长逐步更新物理场以避免对象完全相互绕过,这将是直接的,每一步一次。

    但是,坚持显式更新方案(速度时间步长)仅适用于低速度。从许多基于物理的游戏中可以看出,提高速度通常会导致物体穿透。 大多数简单的物理引擎选择只允许这些错误,因为强大的collision detection 众所周知是困难且昂贵的。

    如果将适用性限制为圆形,则可以做一些事情。写出对象的距离,作为时间的函数(在时间步长上)

    x_a = x0_a + v_a*t
    x_b = x0_b + v_b*t
    d_ab = norm(x_a-x_b) = norm(x0_b-x0_a + (v_b-v_a)*t) = norm(dx + dv*t)
    

    如果距离函数d_ab 在时间步长内等于任何t 的半径r_a+r_b 之和,那么你就有碰撞。因此,首先检查哪些对象发生碰撞(如果有),然后从该点继续并重新进行分析,直到到达时间步的末尾。相当复杂,仍然只适用于圆形/球体。

    【讨论】:

    • 我曾想过使用类似的方法,但这似乎真的很昂贵。此外,由于速度“长度”的差异以及时间步长中可能发生的差异,无法保证这将始终有效。
    • 我回答的第二部分详细介绍了您应该如何分析找到碰撞时间。你需要解决td_ab = r_a+r_b。对于比几个圈更复杂的事情,这变得非常乏味且成本​​极高。我不认为任何游戏引擎会这样做,他们要么选择非鲁棒的显式方法,要么伪造它。
    【解决方案4】:

    这里的碰撞检测关键字是“连续”或“不连续”。

    就像其他一些答案一样,大多数现代碰撞库都使用非连续版本,因为分析许多碰撞形状的确切碰撞时间成本很高。这意味着您让物体相互渗透,检测到这一点,然后调整它们的速度以对抗相互渗透。就像已经注意到的那样,这允许快速移动的物体相互通过。这可以通过增加运行物理的频率来解决,本质上您需要将其与渲染分离(使用更快的计时器运行它)。您解决互穿等问题的实际方法在另一个问题的范围内。

    但是,对于圆,您可以解析求解任何具有恒定(或加速)速度的圆对的确切碰撞时间这一事实使您有可能在这种特殊情况下进行连续碰撞检测。这将是一个不错的项目,但我不确定这是否是正确的方式,因为您可能突然想要包含其他形状,然后您就被卡住了。您必须解决的公式已作为答案发布。

    除了总结这两种方法之外,我想在这里补充的是,因为您似乎非常关心“现代计算系统”的处理速度,现在的计算机即使以幼稚的方式进行任何这些计算的速度都非常快如果您稍微优化内部代码,则检查所有圆对。与渲染相比,提高物理速度可能完全没有问题,以解决高速的非连续情况。

    为了避免对所有对象进行配对检查,非连续引擎添加的内容是使用某种数据结构来跟踪对象及其在空间中的一般位置和占用情况,以检测潜在重叠而不必须对照每个对象检查每个对象。这在商店谈话中称为broadphase。如果你开始增加对象的数量,你最终肯定需要实现这样的东西。您没有说明“大量”圆圈是多少或您所针对的 CPU,因此希望您可以跳过此步骤并以简单的方式作为开始,但除非您发布您的用例,否则很难说。

    底线:我会选择久经考验的非连续发动机类型,以足够的物理频率来避免高速跳跃的情况,并且不用担心性能。如果您真的有兴趣为游戏编写代码,并且不想关心细节,您可以简单地使用一些现有的 2D 物理库,例如 ChipmunkBox2D。我不熟悉它们的实现,但如果您研究代码,我相信它们也是学习该主题的好方法。

    【讨论】:

    • 我们应该将思想扩展到复杂的 3D 网格:我举了圆圈的例子来保持简单,但我真的是把它当作一般情况。我想制作一个引擎,这样我就可以在游戏中使用它了图形层,我过去做过,但这并不能保证任何事情,只是有帮助。最重要的是,它似乎也相当昂贵。
    猜你喜欢
    • 1970-01-01
    • 2017-09-20
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    相关资源
    最近更新 更多