碰撞引擎如何工作?


78

碰撞引擎如何工作?

这是一个极为广泛的问题。什么代码使事物相互碰撞,什么代码使玩家走进墙壁而不是穿过墙壁?代码如何不断刷新玩家的位置和物体的位置,以保持重力和碰撞正常进行?

如果您不知道碰撞引擎是什么,基本上它通常用于平台游戏中,以使玩家疯狂撞墙等。有2D类型和3D类型,但是它们都完成相同的事情:碰撞。

那么,是什么让碰撞引擎滴答答答呢?


3
您可以使用边界框和球体作弊,而边界框和球体的交点可以快速确定。然后,您可以更仔细地检查。cs.unc.edu/~dm/collision.html en.wikipedia.org/wiki/Collision_detection 你总是可以用天真的算法做到这一点慢。Comp Geometry具有一些技巧,可以利用问题的几何性质,并使算法更快。这是一篇非常好的论文:cs.hku.hk/research/techreps/document/TR-2005-01.pdf

什么是碰撞引擎

4
@gnat碰撞引擎基本上是游戏中使用的引擎(通常),这样您的玩家(称他为bob)无论何时bob进入墙壁,bob停止,bob都不会穿过墙壁。他们通常还会处理游戏中的重力以及类似的环境问题。
JXPheonix 2012年

Answers:


172

碰撞引擎和物理引擎之间有很大的区别。尽管物理引擎通常依赖于碰撞引擎,但是它们并没有做同样的事情。

然后将碰撞引擎分为两部分:碰撞检测和碰撞响应。后者通常是物理引擎的一部分。这就是为什么碰撞引擎和物理引擎通常会合并到同一库中的原因。

碰撞检测有两种形式,离散形式和连续形式。先进的引擎支持这两者,因为它们具有不同的属性。通常,连续碰撞检测非常昂贵,并且仅在真正需要的地方使用。大多数碰撞和物理学都是使用离散方法来处理的。在离散方法中,对象最终会相互渗透,然后物理引擎将它们推开。因此,引擎实际上并不会阻止游戏者部分地穿过墙壁或地板,而只是在检测到游戏者部分位于墙壁/地板中后才将其固定。在这里,我将重点介绍离散冲突检测,因为这是我从头开始实现的最丰富经验。

碰撞检测

碰撞检测相对容易。每个对象都有一个变换和一个形状(可能有多个形状)。天真的方法会使碰撞引擎对所有对象对进行O(n ^ 2)循环,并测试对象对之间是否存在重叠。在更智能的方法中,存在多个空间数据结构(例如,针对静态对象与动态对象),每个对象的边界形状以及每个对象的多部分凸子形状。

空间数据结构包括KD树,动态AABB树,八叉树/四叉树,二进制空间分区树等。每个引擎都有其优点和缺点,这就是为什么某些高端引擎使用多个引擎的原因。例如,动态AABB树确实非常快,并且非常适合处理许多移动的对象,而KD-Tree可能更适合对象碰撞的静态关卡几何。还有其他选择。

广义阶段为每个对象使用空间数据结构和抽象边界量。包围体积是一种简单的形状,它包围了整个对象,通常目标是尽可能“紧密”地包围它,同时保持便宜以进行碰撞测试。最常见的边界形状是“轴对齐的边界框”,“对象对齐的边界框”,“球体”和“胶囊”。AABB通常被认为是最快和最容易的(在某些情况下球体更容易,更快,但是无论如何,这些空间数据结构中的许多都需要将球体转换为AABB),但它们也往往很难适应许多对象。胶囊在3D引擎中很流行,用于处理字符级碰撞。有些引擎会使用两个边界形状

碰撞检测的最后一个阶段是精确检测几何图形相交的位置。尽管并非总是如此,这通常意味着使用网格(或2D中的多边形)。此阶段的目的是找出对象是否真的发生碰撞,是否需要精细的细节(例如,射击者的子弹碰撞,在此您希望能够忽略几乎不会错过的射击),以及还可以找出对象碰撞的确切位置,这将影响对象的响应方式。例如,如果一个盒子坐在桌子的边缘,引擎必须知道桌子在盒子的哪个点上推。根据盒子悬挂的距离,盒子可能会开始倾斜并掉落。

联系歧管生成

这里使用的算法包括流行的GJK和Minkowski Portal精炼算法,以及“分离轴”测试。由于流行的算法通常仅适用于凸形,因此有必要将许多复杂的对象分解为凸形子对象,并对每个对象分别进行碰撞测试。这就是为什么简化网格经常用于碰撞以及减少使用较少三角形的处理时间的原因之一。

这些算法中的某些算法不仅告诉您对象确实发生了碰撞,而且还告知它们发生碰撞的位置-它们相互穿透的距离以及“接触点”的位置。一些算法需要其他步骤(例如多边形裁剪)才能获取此信息。

身体反应

此时,已发现一个联系人,并且有足够的信息供物理引擎处理该联系人。物理处理会变得非常复杂。较简单的算法适用于某些游戏,但即使看起来似乎很简单的方法(如保持一堆盒子稳定)也相当困难,并且需要大量工作和不明显的破解。

在最基本的层次上,物理引擎将执行以下操作:它将处理碰撞对象及其接触流形,并计算分离碰撞对象所需的新位置。它将对象移动到这些新位置。它还将计算此推的速度变化,并结合恢复(弹跳)和摩擦值。物理引擎还将施加任何其他作用在对象上的力(例如重力)来计算对象的新速度,然后(下一帧)它们的新位置。

更高级的物理响应很快变得复杂。上面的方法在许多情况下都会失效,包括一个对象位于另外两个对象之上。单独处理每对将导致“抖动”,并且对象将反弹很多。最基本的技术是对碰撞对象对进行多次速度校正迭代。例如,在其他两个“ B”和“ C”框上方放置一个“ A”框的情况下,将首先处理碰撞AB,使框A进一步倾斜到框C中。然后在晚上处理AC碰撞开箱即用,但将A下拉到B。然后进行另一个迭代,因此可以稍微解决由AC校正引起的AB误差,从而在AC响应中产生更多的误差。再次处理AC时处理。完成的迭代次数不是固定的,并且在任何时候它都不会变得“完美”,而是无论任何迭代次数停止给出有意义的结果。10次​​迭代是典型的首次尝试,但需要进行调整才能找出特定引擎和特定游戏需求的最佳数量。

联系缓存

在处理多种类型的游戏时,还有其他技巧很方便(或多或少)。联系人缓存是更有用的一种。使用联系人缓存,每组冲突对象都保存在查找表中。在检测到冲突时,每帧都将查询此高速缓存,以查看先前是否接触过对象。如果对象先前未接触过,则可以生成“新碰撞”事件。如果对象先前已接触,则可以使用该信息提供更稳定的响应。联系人缓存中未在框架中更新的任何条目都表示两个对象已分离,并且可以生成“分离对象”事件。游戏逻辑通常可用于这些事件。

游戏逻辑还可以响应新的碰撞事件并将其标记为已忽略。这对于实现平台中的一些常见功能确实很有帮助,例如可以跳升但可以站立的平台。天真的实现可能只是忽略具有向下平台的碰撞->角色正常碰撞(表明玩家的头部撞到平台的底部),但是如果没有接触缓存,如果玩家的头部在平台上戳了一下,然后他开始,则碰撞会中断跌倒。在那一点上,接触法线可能最终指向上方,导致玩家在不应该通过平台时弹出。通过接触缓存,引擎可以可靠地观察初始碰撞法线,而忽略所有其他接触事件,直到平台和玩家再次分离为止。

睡眠

另一种非常有用的技术是如果对象不与之交互,则将其标记为“睡眠中”。睡眠中的对象不会获得物理更新,不会与其他睡眠中的对象发生碰撞,基本上只是及时冻结在那里,直到另一个非睡眠中的对象与它们发生碰撞。

这样的影响是,所有坐在那里什么都不做的碰撞对象对都不会占用任何处理时间。另外,由于没有恒定的微小物理校正量,因此堆栈将保持稳定。

当一个物体的速度接近零且超过一帧时,它就是睡眠的候选者。请注意,用于测试该接近零速度的epsilon可能会比通常的浮点比较epsilon稍高,因为您应该期望堆叠的对象产生一些抖动,并且如果它们重叠,您希望整个对象进入睡眠状态重新保持“足够接近”稳定。该阈值当然需要调整和试验。

约束条件

许多物理引擎的最后一个主要方面是约束求解器。这种系统的目的是促进诸如弹簧,电动机,轮轴,模拟软体,布料,绳索和链条之类的东西的实现,甚至有时甚至是流体的实现(尽管流体通常被实现为完全不同的系统)。

甚至约束解决的基础知识都可能需要大量数学知识,并且超出了我在该主题上的专业知识。我建议阅读Randy Gaul的出色物理学文章系列,以对该主题进行更深入的解释。


如果您要解决冲突解决方案的最小数量问题,那么考虑到在复杂的设置中允许大量冲突休止符将大大降低帧速率,那么您可能还应该解决将冲突解决方案数量保持在最低水平的需求。那么当您谈论迭代次数时,是每对迭代次数,还是所有冲突的迭代次数。
gardian06 2012年

1
@ gardian06:这是一个快速撰写的概述,请确保可以对其进行扩展。例如,我忘记提及对象睡眠,这很有用。对于迭代,我遍历了所有对对,即使迭代计数相对较大(20+),我也从未注意到性能问题(但同样,这与对象休眠有关,因此要解决的活动冲突数量相对较少) )。
肖恩·米德迪奇

1
神奇的答案,+ 1。阅读此书确实使我想实现这些算法。
Miles Rout

3

一般问题:确定对象的所有可能组合中的哪一个具有非零的相交体积。

天真的通用方法很简单:对于每个可能的对象对,计算相交的体积。这通常是不实际的,因为它需要O(n ^ 2)相对昂贵的相交操作。

因此,实际的实施通常是专门的,做出某些假设以允许避免相交检查或降低其成本。空间分区利用了以下事实:对象通常相对于总体积较小,并且通常会减少与O(n log n)的比较次数。轴对齐的边界框和边界球可以提供便宜的粗相交检查,只要对象遵守某些紧凑性假设即可。等等。


2

我使用的一个“碰撞引擎” 非常容易掌握。

基本上,API提供了一种具有method的对象collidesWith,从而

  1. 它的参数是“适合”碰撞的不同种类的对象
  2. 它返回true还是false取决于是否发生碰撞
  3. 有一个选项允许选择对象的所有边界矩形是触发碰撞事件还是仅触发这些矩形内的不透明像素(像素级检测)

在我的应用程序中,我只是定期调用collidesWith以查明是否发生了碰撞。

是不是很简单?


也许唯一需要一点想象力的事情就是当普通的边界矩形不足以模拟碰撞对象的几何形状时。在这种情况下,只需使用多个可碰撞对象而不是一个即可。

通常,当您发现单个碰撞矩形无法满足您的需要时,您便发明了一种将事物分解为更多矩形子元素的方法,以便在组合时,这些元素可以模拟/近似所需的行为。

  • 最终用户只是不在乎幕后有多少物体。只要最终结果能像他们期望的那样,他们就很高兴,例如,像是一栋有后院围栏的房子:

    http://i.stack.imgur.com/4Wn5B.jpg


2

我认为您对正在谈论的内容有些困惑,并且在谈论一些不同的事情。

说这个项目从位置X移到位置Y的能力是基于物理原理的(这也攻击了它们如何移动以及为什么移动)

用于冲突检测的方法是根据游戏的结构确定的。如果您的游戏是一个开放的大世界,那么您应该非常考虑空间分区(四叉树(用于3D的八叉树),BSP,传统网格或老式的测试一切方法)

实现碰撞检测系统的最佳方法是分步骤进行。

  1. 将所有对象放入通用的边界体积/形状中,然后进行测试

  2. 如果1通过,则重复进行更复杂的体积/形状重复,直到准备测试绝对几何

  3. 测试绝对几何形状将根据形状的复杂程度以及这些形状的精确度确定重复步骤2的时间。

您应该考虑将这些步骤中的每个步骤都提早进行,并且是为了消除所发生的碰撞,并且只有在它们确实接触时才在步骤3中返回true。

最后是碰撞解决。这确定了您发现碰撞并证明它确实是碰撞之后发生的事情,以及如何处理。这通常由物理学处理。

传统循环如下所示:

receive input
update physics
collision detection
collision resolution
render
repeat

我只想指出,游戏引擎很少会测试绝对几何图形是否发生碰撞。通常,算法只会执行大纲中的第2步。
kevintodisco,2012年

大多数游戏引擎会在许多(并非全部)情况下测试绝对几何形状。否则,物理响应中将出现非常明显的“毛刺”。大多数引擎将具有一个简单的广义阶段(空间分区),一个简单的边界体积测试(最常见的是AABB),然后(必要时)进行一个简化的几何图形测试(例如,与全LOD渲染几何图形不同的几何图形,但是仍然是原始几何体,可能是渲染网格的低LOD版本之一。
肖恩·米德迪奇

@seanmiddleditch我的主要目的是通常在测试近似值时会尽量避免测试凹面多边形/多面体。
gardian12年

@ktodisco取决于图形的凹度及其精确度,然后需要为物理系统解决碰撞所需的返回值,因为这可能会根据物理引擎和预期的精度而有所不同。回应
gardian12年

@ guardian06 seanmiddleditch的解释要可行得多,尽管测试由数千个多边形组成的字符之间的绝对交点仍然不是一个好主意。
kevintodisco,2012年

1

在图形卡级别(您通常要处理三角形),总体思路是对场景进行某种划分,从而不必检查所有N个三角形(这可以作为预处理步骤完成) ),然后找出您在场景中的位置,然后仅检查该分区中的那10-50个三角形。

有关更多信息,请参见BSP和Kd树。还有其他分区方法。


0

首先,我认为碰撞引擎最重要的工作是逐帧确定在任何特定情况下不需要检查哪些碰撞,并从进一步的检查中剔除那些对象。

其次,但也很重要,以更详细(准确)的方式检查在第一步中未剔除的其余对象。

第三,利用最有效/最适当的方法进行检查。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.