高速移動オブジェクトの六角形衝突検出?


39

オブジェクトには位置と速度ベクトルがあります。通常、2つのオブジェクトが衝突するかどうかを確認するために位置のみが使用されます。これは、最初の衝突チェックで最初のオブジェクトの前にあり、その後ろで2番目の衝突チェック。

BoundingBoxの衝突が失敗する

また、ラインベースの衝突チェックもあります。このチェックでは、各オブジェクトの移動ベクトルが他のオブジェクトの境界ボックスと交差するかどうかのみをチェックします。これは、ポイントの拡張として見ることができます。ただし、高速で移動するオブジェクトが本当に小さい場合にのみ機能します。

六角衝突勝利

だから、私のアイデアは、ポイントを拡大するのではなく、なぜ長方形を拡大しないのですか?これにより、六角形になります。

今、これまでのところとても良い。しかし、この種の2つの六角形が交差するかどうかを実際に確認するにはどうすればよいですか?これらは非常に特殊な六角形であることに注意してください。

六角形の仕様

ボーナス質問:衝突が発生した場所を正確に(または、ある程度の時間後に)計算することは可能ですか?これは、実際に何がどこで、どのくらいのパワーで発生したかを検出し、衝突からフレームの終わりまでの時間での動きをシミュレートするのに非常に便利です。


for(Aのライン)for(Bのライン)if(ラインが交差する)衝突-ただし、BのAまたはAの場合のBはカバーしません。ふむ =)
ヤリコンパ

4
あなたは箱にコミットしていますか?描いたボックスは、精度の損失を最小限に抑えた円で表すことができますが、衝突アルゴリズムは比較的簡単です。スイープサークル衝突検出を検索します。あなたの長さ/幅の比率が1から離れると、これは魅力的ではなくなります。
スティーブH

@SteveH最も柔軟なソリューションを探しているので、長さ/幅の比率はかなり大したことです。
APIビースト

1
六角形が交差するからといって、衝突が発生するわけではないことに注意してください。それらが交差するかどうかを間違えずに伝えることができたとしても、衝突があるかどうか、そして明らかに、いつどこで発生するかを判断するためにやるべきことがあります。そのため、まだボーナス質問にジャンプできません。
jrsala

2
私はこれを試したことがありませんが、2D空間の六角形の代わりに、2Dの動きを1つの軸が時間である3D空間のボリュームと考えることができるようです。次に、2つの3D多面体を(x、y、t)座標と交差させます。2つのソリッドオブジェクトが交差する場合、最小t値を見つけたいと思います。Bのすべての座標をAの参照フレームに変換することにより、少し単純化できます。私はこれを実装していませんが、そこから始めます。
amitp

回答:


34

解決策は実際には予想よりも簡単です。トリックは、六角形の手法の前にミンコフスキー減算を使用することです。

ここにあなたの長方形AとBはその速度で、あるvAvBvAおよびvBは実際には速度ではなく、1フレーム中に移動した距離であることに注意してください。

ステップ1

ここで、長方形BをポイントPに、長方形Aを長方形C = A +(-B)に置き換えます。これは、寸法AとBの合計です。ミンコフスキー追加プロパティは、点と新しい長方形の衝突が発生することを示しています元の2つの四角形の間で衝突が発生した場合にのみ:

ステップ2

しかし、長方形CがベクトルvAに沿って移動し、ポイントPがベクトルに沿って移動する場合vB、参照フレームの簡単な変更は、長方形Cがまだあり、ポイントPがベクトルに沿って移動した場合と同じであることを示しvB-vAます:

ステップ3

次に、単純なボックスとセグメントの交差式を使用して、新しい参照フレームで衝突が発生した場所を特定できます。

最後のステップは、適切な参照フレームに戻ることです。円で囲まれた交差点までのポイントの移動距離をベクトルの長さで除算vB-vAすると、sそのような値が得られます0 < s < 1。衝突が一度起こるあなたのフレームの持続時間です。s * TT

madshogoのコメント
ビースト氏自身の答えよりもこの手法の大きな利点の1つは、回転がない場合、「ミンコフスキー減算」A +(-B)後続のすべてのタイムステップで 1回計算できることです。

したがって、このすべてに時間がかかる唯一のアルゴリズム(ミンコフスキー和、複雑さO(mn)、ここでmAの頂点の数、nBの頂点の数)は、1回だけ使用でき、衝突検出を効果的に一定にします。時間の問題!

後で、ABが(クアッドツリーの)シーンの異なる部分にあり、衝突しないことを確認したら、合計を捨てることができます。

対照的に、ビースト氏の方法は、各タイムステップで非常に多くの計算を必要とします。

また、軸に沿った長方形の場合、A +(-B)は、頂点ごとにすべての合計を実際に計算するよりもはるかに簡単に計算できます。ちょうど展開Aの高さ加算することによりBの高さと幅Bの幅(各側半分)。

しかし、これはすべて、ABも回転しておらず、両方が凸である場合にのみ機能します。回転がある場合、または凹形状を使用する場合は、スイープボリューム/領域を使用する必要があります。
コメントの終わり


4
かなり興味深いアプローチのように見えますが、私はまだ100%を把握していません。オブジェクトが本当に小さく、2行間を移動している場合はどうなりますか?i.imgur.com/hRolvAF.png
APIビースト

-1:この方法では、衝突が発生することを確認できません。セグメントと押し出されたボリュームが交差しない場合にのみ、それ発生しないことを確認することできます。しかし、それらが交差し、それでも衝突がないことは完全に可能です。間違っているのは、「[...]単純なセグメント間交差を使用して、衝突が発生した場所を判断できるようになった」という部分です。
jrsala

2
@madshogoそうですね。タイムステップはオブジェクトサイズに比べて十分に小さいため、これは問題になりませんが、一般的なケースでは確かにそれほど堅牢ではありません。修正を検討します。
サムホセバー

@SamHocevar答えを修正できたら素晴らしいと思います。
APIビースト

1
yesおよびno @LuisAlves ...すべてのロジックの作品は、しかし、あなたは交換する必要がありますvB-vAg(t)-f(t)場所fg時間をかけてAとBの位置です。これはもはや直線ではないため、ボックスを解く必要があります—パラメトリック曲線の交差の問題。
サムホセバー14年

17

まず第一に、軸に揃えられた長方形の場合、Kevin Reidの答えが最良であり、アルゴリズムが最速です。

第二に、単純な形状の場合、相対速度(下記参照)と衝突検出のための分離軸定理を使用します。それはなり衝突が直線運動(回転なし)の場合に起こるかどうかを教えてくれ。また、回転がある場合、正確に回転させるには小さなタイムステップが必要です。さて、質問に答えるために:


一般的なケースで、2つの凸形状が交差するかどうかを判断する方法は?

六角形だけでなく、すべての凸形状で機能するアルゴリズムを紹介します。

仮定X及びYは、 2つの凸形状です。彼らは、ポイントがある場合交差し、それらは共通点がある場合にのみ、すなわち、X Xと点Y∈Yように、X = Yが。空間をベクトル空間とみなす場合、これはx-y = 0と言うことになります。そして今、私たちはこのミンコフスキー事業に着きます:

ミンコフスキー和X及びYは、すべての集合であるX + Yのためのx∈XY∈Y


XとYの例


X、Yおよびそれらのミンコフスキー和、X + Y

仮に(-Y)全ての集合である-yY∈Yは、次に前の段落、所与のX及びYが交差場合にのみX +(Y)含有0を、原点です、

補足:なぜX-Yの代わりにX +(-Y と書くのですか?まあ、数学では、そこのミンコフスキー差と呼ばれる操作であるため、AB、時々書かれているX - Yまだ全てのセットとは何の関係もありませんのx - yのためのx∈XY∈Y (本当のミンコフスキーを違いはもう少し複雑です)。

したがって、X-Yのミンコフスキー和を計算し、原点が含まれているかどうかを確認します。原点は他のポイントと比較して特別ではないため、原点が特定のドメイン内にあるかどうかを確認するために、特定のポイントがそのドメインに属しているかどうかを判断できるアルゴリズムを使用します。

XYのミンコフスキー和にはクールな特性があります。つまり、XYが凸の場合、X + Yも凸です。そして、点が凸集合に属しているかどうかを見つけることは、その集合が凸でない(知られている)場合よりもはるかに簡単です。

このような点xyには無限大があるため、x∈Xy∈Yのx-yをすべて計算することはできません。XYX + Yは凸であるため、頂点であるシェイプXおよびYを定義する「最も外側の」ポイント、およびX + Yの最も外側のポイント、およびその他のポイントを取得します。

これらの追加ポイントは、X + Yの最も外側のポイントによって「囲まれ」ているため、新しく取得された凸形状の定義に寄与しません。それらは点の集合の「凸包」を定義しないと言います。そのため、原点が凸包内にあるかどうかを判断する最終アルゴリズムの準備として、それらを取り除きます。


X + Yの凸包。「内側」の頂点を削除しました。

したがって、

最初の単純なアルゴリズム

boolean intersect(Shape X, Shape Y) {

  SetOfVertices minkowski = new SetOfVertices();
  for (Vertice x in X) {
    for (Vertice y in Y) {
      minkowski.addVertice(x-y);
    }
  }
  return contains(convexHull(minkowski), Vector2D(0,0));

}

ループは明らかに複雑さO(mn)を持ちます。ここで、mnは各形状の頂点の数です。minkoswkiセットが含まれているMN高々要素を。convexHullこのアルゴリズムは、依存する複雑さがあるあなたが使用するアルゴリズムを、あなたはを目指すことができますO(k個のログ(K))、kは点の集合の大きさなので、我々の場合には、我々が得るO(MNログ(Mn)を)。このcontainsアルゴリズムの複雑さは、凸包のエッジ(2D)またはフェース(3D)の数に比例するため、開始形状に依存しますが、O(mn)より大きくなることはありません。

contains凸形状のアルゴリズムをグーグルで検索します。これは非常に一般的なものです。時間があればここに置いてもいいでしょう。


しかし、それは私たちが行っている衝突検出なので、それを多く最適化することができます

元々、タイムステップdtの間に回転せずに移動する2つのボディABがありました(あなたの写真を見ればわかります)。v Av BをそれぞれABの速度と呼びましょう。これは、持続時間dtのタイムステップ中に一定です。次のものが得られます。

そして、写真で指摘しているように、これらのボディは移動するにつれて領域(または3Dではボリューム)をスイープします

そして、タイムステップ後、A 'B'になります。

ここで単純なアルゴリズムを適用するには、スイープボリュームを計算するだけで済みます。しかし、私たちはこれをしていません。

Bの参照フレームでは、Bは移動しません(ダウ!)。また、Aに v A -v Bを計算することで得られるBに対する特定の速度があります(逆を実行し、Aの参照フレームでBの相対速度を計算できます)。

相対運動

左から右:ベース参照フレームの速度。相対速度; 相対速度の計算。

Bを独自の参照フレームで不動と見なすことにより、相対速度v A -v Bで dtの間に移動するときにAがスイープするボリュームを計算するだけで済みます

これにより、ミンコフスキーの和の計算に使用される頂点の数が(ときどき)大幅に減少します。

別の可能な最適化は、ボディの1つによってスイープされたボリュームを計算するポイントです。Aとしましょう。Aを構成するすべての頂点を変換する必要はありません。エッジ(3Dの面)に属するもののみ外側の法線「掃引の方向」に直面します。正方形の掃引面積を計算したときにすでに気づいていました。法線が掃引方向に向いているかどうかは、掃引方向との内積を使用して判断できます。これは正でなければなりません。

交差点に関するあなたの質問とは関係のない最後の最適化は、私たちの場合に本当に役立ちます。これは、前述の相対速度と、いわゆる軸分離法を使用しています。きっとあなたはすでにそれについて知っています。

あなたが知っていると仮定し、半径ABそれぞれについての質量中心のように、(、と言って質量の中心部とそれから頂点最も遠い距離です):

衝突は、Aの境界円がBの境界円と一致する可能性がある場合にのみ発生します。次の図のように、C BからIまでの距離を計算し、ABの半径の合計よりも大きいことを確認することをコンピューターに伝える方法をここで確認します。大きければ、衝突はありません。小さい場合は、衝突します。

これは、かなり長い形状ではあまりうまくいきませんが、正方形やその他の形状の場合、衝突を除外するのは非常に良い発見的方法です。

ただし、Bに適用される分離軸定理とAによってスイープされるボリュームは、衝突が発生するかどうかを示します。関連するアルゴリズムの複雑さは、各凸形状の頂点の数の合計に比例しますが、実際に衝突を処理するときが来ると、それほど魔法ではなくなります。

交差点を使用し衝突を検出するの役立つ新しいアルゴリズムですが、衝突が発生したかどうか実際に判断するための分離軸定理ほど優れていません

boolean mayCollide(Body A, Body B) {

  Vector2D relativeVelocity = A.velocity - B.velocity;
  if (radiiHeuristic(A, B, relativeVelocity)) {
    return false; // there is a separating axis between them
  }

  Volume sweptA = sweptVolume(A, relativeVelocity);
  return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));

}

boolean radiiHeuristic(A, B, relativeVelocity)) {
  // the code here
}

Volume convexHull(SetOfVertices s) {
  // the code here
}

boolean contains(Volume v, Vector2D p) {
  // the code here
}

SetOfVertices minkowskiMinus(Body X, Body Y) {

  SetOfVertices result = new SetOfVertices();
  for (Vertice x in X) {
    for (Vertice y in Y) {
      result.addVertice(x-y);
    }
  }
  return result;

}

2

「六角形」を使用することはそれほど役立つとは思いません。以下は、軸に沿って配置された長方形の正確な衝突を取得する方法のスケッチです。

X座標範囲が重なり、Y座標範囲が重なる場合にのみ、2つの軸に沿った長方形が重なります。(これは、分離軸定理の特殊なケースと見なすことができます。)つまり、X軸とY軸に長方形を投影すると、2つの線と線の交点に問題が減ります。

1つの軸上の2本の線が交差する時間間隔を計算し(たとえば、時間(オブジェクトの現在の分離/オブジェクトの相対的接近速度)で開始)、他の軸についても同じことを行います。それらの時間間隔が重複する場合、重複内の最も早い時間は衝突の時間です。


3
スケッチを忘れました。
マイケルハウス

2
@ Byte56いいえ、私はそれがアルゴリズムのスケッチであり、擬似コードでさえないことを意味します。
ケビンリード

ああなるほど。私の間違い。
マイケルハウス

これは実際には最も簡単な方法です。対応するコードを追加して実装しました。
パシャ

1

四角形よりも辺が多いポリゴンの衝突を計算する簡単な方法はないと思います。私はそれを線や正方形のような原始的な形に分解します。

function objectsWillCollide(object1,object2) {
    var lineA, lineB, lineC, lineD;
    //get projected paths of objects and store them in the 'line' variables

    var AC = lineCollision(lineA,lineC);
    var AD = lineCollision(lineA,lineD);
    var BC = lineCollision(lineB,lineC);
    var BD = lineCollision(lineB,lineD);
    var objectToObjectCollision = rectangleCollision(object1.getRectangle(), object2.getRectangle());

    return (AC || AD || BC || BD || objectToObjectCollision);
}

オブジェクトのパスと終了状態の図

前回の計算中にチェックされるはずだった各オブジェクトの開始状態をどのように無視するかに注意してください


3
これに関する問題は、オブジェクトのサイズが大きく異なる場合、小さなオブジェクトが衝突を引き起こすことなく大きなオブジェクトのパス内を移動できることです。
APIビースト

0

分離軸定理

別の軸定理は、「2つの凸形状が交差しない軸を見つけることができれば、2つの形状は交差していない」、またはITにとってより実用的であると言います

「2つの凸形状は、可能なすべての軸で交差する場合にのみ交差します。」

軸に揃えられた長方形の場合、xとyの2つの軸が考えられます。しかし、定理は長方形に限定されず、形状が交差できる他の軸を追加するだけで、任意の凸形状に適用できます。トピックの詳細については、Nの開発者によるこのチュートリアルをご覧ください。http//www.metanetsoftware.com/technique/tutorialA.html#section1

実装は次のようになります。

axes = [... possible axes ...];
collision = true;
for every index i of axes
{
  range1[i] = shape1.getRangeOnAxis(axes[i]);
  range2[i] = shape2.getRangeOnAxis(axes[i]);
  rangeIntersection[i] = range1[i].intersectionWith(range2[i]);
  if(rangeIntersection[i].length() <= 0)
  {
    collision = false;
    break;
  }
}

軸は、正規化されたベクトルとして表すことができます。

範囲は1次元の線です。開始点は最小の投影点に、終了点は最大の投影点に設定する必要があります。

「掃除された」長方形にそれを適用する

問題の六角形は、オブジェクトのAABBを「掃く」ことによって生成されます。スイープは、任意の形状に1つの可能な衝突軸、つまり移動ベクトルを追加します。

shape1 = sweep(originalShape1, movementVectorOfShape1);
shape2 = sweep(originalShape2, movementVectorOfShape2);

axes[0] = vector2f(1.0, 0.0); // X-Axis
axes[1] = vector2f(0.0, 1.0); // Y-Axis
axes[2] = movementVectorOfShape1.normalized();
axes[3] = movementVectorOfShape2.normalized();

これまでのところ、2つの六角形が交差するかどうかを確認できます。しかし、それはさらに良くなります。

このソリューションは、凸形状(三角形など)およびスイープされた凸形状(スイープされた八角形など)に対して機能します。ただし、形状が複雑になるほど、効果が低下します。


ボーナス:魔法が起こる場所。

前述したように、追加の軸は移動ベクトルのみです。運動は時間に速度を乗じたものであるため、ある意味ではそれらは単なる空間軸ではなく、時間空間軸です。

これは、これらの2つの軸から衝突が発生した可能性のある時間を導出できることを意味します。そのためには、移動軸上の2つの交差点間の交差点を見つける必要があります。ただし、これを行う前に、両方の範囲を正規化する必要があるため、実際にそれらを比較できます。

shapeRange1 = originalShape1.getRangeOnAxis(axes[2]);
shapeRange2 = originalShape2.getRangeOnAxis(axes[3]);
// Project them on a scale from 0-1 so we can compare the time ranges
timeFrame1 = (rangeIntersection[2] - shapeRange1.center())/movementVectorOfShape1.project(axes[2]);
timeFrame2 = (rangeIntersection[3] - shapeRange2.center())/movementVectorOfShape2.project(axes[3]);
timeIntersection = timeFrame1.intersectionWith(timeFrame2);

この質問をしたとき、この方法でいくつかのまれな誤検出が生じるという妥協点をすでに受け入れました。しかし、私は間違っていました。今回の交差点を確認することで、衝突が「実際に」発生したかどうかをテストでき、それらの誤検知を整理できます。

if(collision)
{
  [... timeIntersection = see above ...]
  if(timeIntersection.length() <= 0)
    collision = false;
  else
    collisionTime = timeIntersection.start; // 0: Start of the frame, 1: End of the frame
}

コード例のエラーに気づいたら教えてください。まだ実装していないので、テストできませんでした。


1
解決策を見つけて、おめでとうございます!しかし、前に言ったように、六角形が交差するからといって、衝突が発生するわけではありません。メソッドを使用して、必要なすべての衝突時間を計算できます。衝突がなければ、あまり役に立ちません。次に、相対速度を使用して、1つの掃引ボリュームのみを計算し、SATを使用する場合の計算を簡素化できます。最後に、shapeRange1 == shapeRange2コードと同様に、インデックスが混同されている可能性があるため、「交差時間」トリックがどのように機能するかについて大まかな考えしかありません。
jrsala

@madshogoは今、もっと意味があるはずです。
APIビースト

範囲の正規化がどのように機能するかはまだわかりませんが、それは写真が必要だからだと思います。私はそれがあなたのために働くことを望みます。
jrsala

-2

スイープエリアが両方とも閉じている限り(エッジラインによって形成される境界にギャップがない)、次のように機能します(衝突テストをline-lineおよびpoint-rect / point-triに減らします)。

  1. エッジが接触していますか?(ラインとラインの衝突)スイープエリアのエッジラインが他のスイープエリアのエッジラインと交差するかどうかを確認します。各掃引エリアには6つの側面があります。

  2. 小さなものは大きなものの中にありますか?(軸に合わせた形状を使用する(point-rect&point-tri))掃引領域の向きを変更(回転)して、大きい方が軸に合わせられ、小さい方が内部にあるかどうかをテストします(コーナーポイント(すべてまたはなし)である必要があります)は、軸に沿ったスイープエリア内にあります)。これは、ヘックスをトリスと長方形に分解することで行われます。

最初に行うテストは、それぞれの可能性に依存します(最も一般的なテストを最初に行います)。

スイープされた境界円(16進数ではなくカプセル)を使用する方が簡単な場合があります。軸に揃えると、2つの半円と四角形に分割しやすくなるためです。..解決策を描いてあげます


長方形の1つが非常に小さく、2つのエッジライン間のスペース内で移動する場合は機能しません。
jrsala

@madshogo返信に追加しました。これで完全なソリューションになるはずです。
軸索

1
「軸に沿ったシェイプを使用する(point-rect&point-tri)」:三角形または「ポイントトライアングル」(とにかく)を軸にどのように揃えますか?「より大きなものが軸に揃えられるように」:どちらがもう一方よりも大きいかをどのように確認できますか?面積を計算しますか?「これは、あなたのヘックスをトリスと長方形に分解することで行われます。」:どのヘックス?二つあります。「(または、あなたに説明してもらいたい場合は、この回答に賛成票を投じてください)」:あなたは本気ですか??
jrsala

「三角形と軸をどのように揃えますか?」A:掃引領域を作成するオブジェクトのパスを揃えます。エッジを選択して、トリガーを使用します。「どのサイズが他のサイズよりも大きいかをどのように確認できますか?A:たとえば、長方形(ヘックスの中央)の対角線上の2つのポイント間の距離を使用します。「どのヘックス?」A:大きなものです。
軸索
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.