まず第一に、軸に揃えられた長方形の場合、Kevin Reidの答えが最良であり、アルゴリズムが最速です。
第二に、単純な形状の場合、相対速度(下記参照)と衝突検出のための分離軸定理を使用します。それはなり衝突が直線運動(回転なし)の場合に起こるかどうかを教えてくれ。また、回転がある場合、正確に回転させるには小さなタイムステップが必要です。さて、質問に答えるために:
一般的なケースで、2つの凸形状が交差するかどうかを判断する方法は?
六角形だけでなく、すべての凸形状で機能するアルゴリズムを紹介します。
仮定X及びYは、 2つの凸形状です。彼らは、ポイントがある場合交差し、それらは共通点がある場合にのみ、すなわち、X ∈ Xと点Y∈Yように、X = Yが。空間をベクトル空間とみなす場合、これはx-y = 0と言うことになります。そして今、私たちはこのミンコフスキー事業に着きます:
ミンコフスキー和のX及びYは、すべての集合であるX + Yのためのx∈XとY∈Y 。
XとYの例
X、Yおよびそれらのミンコフスキー和、X + Y
仮に(-Y)全ての集合である-y用Y∈Yは、次に前の段落、所与のX及びYが交差場合にのみX +(Y)含有0を、原点です、。
補足:なぜX-Yの代わりにX +(-Y )と書くのですか?まあ、数学では、そこのミンコフスキー差と呼ばれる操作であるため、AとB、時々書かれているX - Yまだ全てのセットとは何の関係もありませんのx - yのためのx∈XとY∈Y (本当のミンコフスキーを違いはもう少し複雑です)。
したがって、Xと-Yのミンコフスキー和を計算し、原点が含まれているかどうかを確認します。原点は他のポイントと比較して特別ではないため、原点が特定のドメイン内にあるかどうかを確認するために、特定のポイントがそのドメインに属しているかどうかを判断できるアルゴリズムを使用します。
XとYのミンコフスキー和にはクールな特性があります。つまり、XとYが凸の場合、X + Yも凸です。そして、点が凸集合に属しているかどうかを見つけることは、その集合が凸でない(知られている)場合よりもはるかに簡単です。
このような点xとyには無限大があるため、x∈Xとy∈Yのx-yをすべて計算することはできません。X、Y、X + 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)を持ちます。ここで、mとnは各形状の頂点の数です。minkoswki
セットが含まれているMN高々要素を。convexHull
このアルゴリズムは、依存する複雑さがあるあなたが使用するアルゴリズムを、あなたはを目指すことができますO(k個のログ(K))、kは点の集合の大きさなので、我々の場合には、我々が得るO(MNログ(Mn)を)。このcontains
アルゴリズムの複雑さは、凸包のエッジ(2D)またはフェース(3D)の数に比例するため、開始形状に依存しますが、O(mn)より大きくなることはありません。
contains
凸形状のアルゴリズムをグーグルで検索します。これは非常に一般的なものです。時間があればここに置いてもいいでしょう。
しかし、それは私たちが行っている衝突検出なので、それを多く最適化することができます
元々、タイムステップdtの間に回転せずに移動する2つのボディAとBがありました(あなたの写真を見ればわかります)。v Aとv BをそれぞれAとBの速度と呼びましょう。これは、持続時間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の面)に属するもののみ外側の法線「掃引の方向」に直面します。正方形の掃引面積を計算したときにすでに気づいていました。法線が掃引方向に向いているかどうかは、掃引方向との内積を使用して判断できます。これは正でなければなりません。
交差点に関するあなたの質問とは関係のない最後の最適化は、私たちの場合に本当に役立ちます。これは、前述の相対速度と、いわゆる軸分離法を使用しています。きっとあなたはすでにそれについて知っています。
あなたが知っていると仮定し、半径のAとBそれぞれについての質量中心のように、(、と言って質量の中心部とそれから頂点最も遠い距離です):
衝突は、Aの境界円がBの境界円と一致する可能性がある場合にのみ発生します。次の図のように、C BからIまでの距離を計算し、AとBの半径の合計よりも大きいことを確認することをコンピューターに伝える方法をここで確認します。大きければ、衝突はありません。小さい場合は、衝突します。
これは、かなり長い形状ではあまりうまくいきませんが、正方形やその他の形状の場合、衝突を除外するのは非常に良い発見的方法です。
ただし、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;
}