2D長方形オブジェクトの衝突の方向を検出するにはどうすればよいですか?


11

この質問の後、さらにサポートが必要です。

四角形のどの側から衝突が発生し、それに応じて反応するかをどうやって確認できますか?

すべての側面から衝突している長方形

青い矢印は、ボックスとの衝突の前後にいくつかの円形オブジェクトがたどるパスです。

どうすればこれを計算できますか?

回答:


8

これはあなたの他の質問に基づいているので、長方形が軸に揃っているときの解決策を示します。

まず、現在のオブジェクトの四角形を次の値で作成します。

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

次に、古いオブジェクトの四角形を作成するには(各オブジェクトに保存するか、単に関数に渡すことができる)古いオブジェクトの位置が必要です(衝突していない場合)。

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

ここで、衝突がどこから来たかを知るために、古い位置が衝突領域になかった側と新しい位置がどこにあるかを見つける必要があります。考えると、これは衝突したときに起こることです。衝突しなかった辺が別の長方形に入ります。

以下はその方法です(これらの関数は衝突があると想定しています。衝突がない場合は呼び出さないでください)。

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

リンスとリピート。

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

ここで、他の質問からの衝突応答を実際に使用します。

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

繰り返しになりますが、これは最善の解決策ではないかもしれませんが、これが通常の衝突検出の方法です。


もう一度、あなたは正しかった!; Dありがとう...(次回、ボードのポストカードをもっと送ってください... ^ ___ ^)
NemoStein

ああ悲しいことに、私はそれを何に使うことができるのか分かりませんでした。
Jesse Emond、2011年

7

質問はこの質問と部分的に同じであるため、質問の回答を試すために、私の回答の一部を再利用します。


次の説明をわかりやすくするために、コンテキストといくつかの変数を定義しましょう。ここで使用する表現形式は、おそらくあなた自身のデータの形式に適合しませんが、この方法を理解する方が簡単です(実際に、原理を理解したら、他の種類の表現を使用して次のメソッドを使用できます)。

したがって、Axis Aligned Bounding Box(またはOriented Bounded Box)と移動Entityを検討します。

  • 境界ボックスは4つの側面で構成され、それぞれを次のように定義します
    。Side1 = [x1、y1、x2、y2](2つの点[x1、y1]および[x2、y2])

  • 移動エンティティは、速度ベクトル(位置+速度)として定義されます:
    位置[posX、posY]および速度[speedX、speedY]


次の方法を使用して、AABB / OBBのどちら側がベクトルにヒットするかを判別できます。

  • 1 / AABBの4つの側面を通過する無限線と、エンティティの速度ベクトルを傾斜として使用するエンティティの位置(事前衝突)を通過する無限線の間の交点を見つけます。(衝突点または平行線または重なり合う線に対応する未定義の番号のいずれかを見つけることができます)

  • 2 /交差点(存在する場合)がわかったら、セグメント境界内にあるものを検索できます。

  • 3 /最後に、リストにまだ複数のポイントがある場合(速度ベクトルは複数の側面を通過できます)、交差点からエンティティの原点までのベクトルの大きさを使用して、エンティティの原点から最も近いポイントを検索できます。

次に、単純な内積を使用して衝突の角度を決定できます。

  • 4 /エンティティ(おそらくボール?)と打撃側のベクトルの内積を使用して、衝突の角度を求めます。

----------

詳細:

  • 1 /交差点を見つける

    • a /パラメトリック形式(P(t)= Po + tD)を使用して、無限線(Ax + Bx = D)を決定します。

      点の原点:Po = [posX、posY]
      方向ベクトル:D = [speedX、speedY]

      A = Dy = speedY
      B = -Dx = -speedX
      D =(Po.x * Dy)-(Po.y * Dx)= (posX speedY)-(posY speedX)

      Ax + By = D <====>(speedY x)+(-speedX y)=(posX speedY)-(posY speedX)

      エンティティポイント値を使用して方法を説明しましたが、これは境界ボックスの4辺の無限線を決定する方法とまったく同じです(Po = [x1、y1]およびD = [x2-x1; y2-y1]を使用)代わりに)。

    • b /次に、2つの無限線の交点を見つけるために、次のシステムを解くことができます。

      A1x + B1x = D1 <==速度ベクトルを傾斜としてエンティティポイントを通過する線。
      A2x + B2x = D2 <== AABBサイドを通過するラインの1つ。

      これにより、遮断の次の座標が得られます。

      遮断x =((B 2 * D 1)-(B 1 * D 2))/((A 1 * B 2)-(A 2 * B 1))
      遮断y =((A 1 * D 2)- (A 2 * D 1))/(((A 1 * B 2)-(A 2 * B 1))

      分母((A1 * B2)-(A2 * B1))がゼロの場合、両方の線は平行または重複しています。それ以外の場合は、交差点を見つける必要があります。

  • 2 /セグメントの境界をテストします。これは簡単に確認できるため、これ以上の詳細は必要ありません。

  • 3 /最も近い点を検索します。リストにまだ複数のポイントがある場合、エンティティの原点に最も近い側を見つけることができます。

    • a /交点からエンティティの原点に向かうベクトルを決定する

      V = Po-Int = [Po.x-Int.x; Po.y-Int.y]

    • b /ベクトルの大きさを計算する

      || V || = sqrt(V.x²+V.y²)

    • c /最小のものを見つけます。
  • 4 /どちらの側に当たるかがわかったので、内積を使用して角度を決定できます。

    • a / S = [x2-x1; y2-y1]はヒットされるサイドベクトルであり、E = [speedX; speedY]はエンティティの速度ベクトルです。

      ベクトル内積ルールを使用すると、

      S・E = Sx Ex + Sy Ey
      および
      S・E = || S || || E || cosθ

      したがって、少しこの式を操作することでθを決定できます...

      cosθ=(S・E)/(|| S || || E ||)

      θ= acos((S・E)/(|| S || || E ||))

      S・E = Sx * Ex + Sy * Ey
      || S || = sqrt(Sx²+Sy²)
      || E || = sqrt(Ex²+Ey²)


注:他の質問スレッドで述べたように、これはおそらく最も効率的でも最も簡単な方法でもありません。これは頭に浮かぶものであり、数学の一部が役立つかもしれません。

具体的なOBBの例では検証しませんでした(AABBで検証しました)が、動作するはずです。


6

簡単な方法は、衝突を解決してから、移動するオブジェクトの衝突ボックスを各方向に1ピクセルずつ移動し、どれが衝突を引き起こしたかを確認することです。

「適切に」行い、回転した衝突形状または任意のポリゴンを使用する場合は、分離軸の定理を読むことをお勧めします。たとえば、メタネットソフトウェア(Nゲームを作った人々)は、SATに関する素晴らしい開発記事を持ってます。また、関係する物理学についても議論します。


2

1つの方法は、長方形を中心に世界を回転させることです。この場合の「世界」は、気になるオブジェクト、つまり長方形とボールだけです。境界がx- / y-axesと揃うまで長方形を中心の周りに回転させ、次に同じ量だけボールを回転させます。

ここで重要な点は、長方形ではなく、長方形の中心を中心にボールを回転させることです。

次に、他の回転していない長方形と同じように、衝突を簡単にテストできます。


別のオプションは、四角形を4つの別個の線分として扱い、それぞれとの衝突を個別にテストすることです。これにより、衝突をテストし、どの面が同時に衝突したか把握できます。


1

私は計算に固定角度を使用しましたが、これはあなたに役立つはずです

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.