1. 無職の学び舎
  2. >
  3. 無職はゲーム数学の勉強をする
  4. >
  5. 点と矩形(回転あり)の衝突

点と矩形(回転あり)の衝突

ここでは点と回転する矩形の衝突判定についてまとめていきます。

回転しない矩形との判定は簡単ですが、矩形がまわり始めると突然難易度があがります。

とはいえ、コツをつかんでしまえばそんなに難しくもないので、1つ1つ解説していきます。

考え方

回転する矩形があり、その中に 点 があるかどうかを判定するには分離軸判定という手段を使います。

分離軸判定とは、ある点がある軸の右にあるのか左にあるのかを判定する方法で、ベクトルの外積を使って判定することができます。

まず考え方として、点が矩形の中にあるということは

点 は 少なくとも、赤い軸の右側(黄色の範囲)にあるということになります。

かつ、右側の赤い軸の右側(黄色の範囲)でもあるという事になります。

さらに、下側の赤い軸の右側(黄色の範囲)でもなければいけません。

さらにさらに、左側の赤い軸の右側(黄色の範囲)でもなければいけません。

という具合に、点が矩形の4辺全ての右側にあれば、点は矩形の中にあると判定する事ができます。

計算

上で説明した内容とほとんど同じですが、計算の流れを見ていきます。

矩形の4隅の座標をそれぞれ、$P_{1}, P_{2}, P_{3}, P_{4}$ とします。

$P$ が $\vec{P_{1}P_{2}}$ より右側にあるとき、$\vec{P_{1}P_{2}}$ と $\vec{P_{1}P}$ の外積はマイナスになります。

次に判定する辺を変えて、$P$ が $\vec{P_{2}P_{3}}$ より右側にあるとき、$\vec{P_{2}P_{3}}$ と $\vec{P_{2}P}$ の外積はマイナスになります。

また辺を変えて、$P$ が $\vec{P_{3}P_{4}}$ より右側にあるとき、$\vec{P_{3}P_{4}}$ と $\vec{P_{3}P}$ の外積はマイナスになります。

さらに辺を変えて、$P$ が $\vec{P_{4}P_{1}}$ より右側にあるとき、$\vec{P_{4}P_{1}}$ と $\vec{P_{4}P}$ の外積はマイナスになります。

というように、4辺全てについて $P$ が右側にある事を調べて、$P$ が全ての辺の右側にあれば $P$ は矩形の中にあるとなります。

まとめ

$\vec{P_{1}P_{2}} \times \vec{P_{1}P} < 0 かつ$
$\vec{P_{2}P_{3}} \times \vec{P_{2}P} < 0 かつ$
$\vec{P_{3}P_{4}} \times \vec{P_{3}P} < 0 かつ$
$\vec{P_{4}P_{1}} \times \vec{P_{4}P} < 0 であれば P は矩形に含まれる(衝突している) $

サンプルコード

/**
 * 点と矩形(回転あり)が当たっているかどうか
 * @param point 点
 * @param box 矩形(回転あり)
 */
function intercect(point:Vector2, box:Box) 
 {
  // 矩形の4辺と、辺の始点から点に向かうベクトルのセット
  const datas = [
    { v1: box.v1to2, v2: Vector2.sub(point, box.p1) },
    { v1: box.v2to3, v2: Vector2.sub(point, box.p2) },
    { v1: box.v3to4, v2: Vector2.sub(point, box.p3) },
    { v1: box.v4to1, v2: Vector2.sub(point, box.p4) }
  ];
 
  // 分離軸判定
  // 矩形の各辺をv1、辺の始点から点に向かうベクトルをv2として
  // v1とv2の外積が正の値だった場合はその時点で矩形の範囲外になるので判定を終了する
  // 4辺全てに対し、外積の結果がマイナスだった場合は矩形の中に点がある
  for(const data of datas) 
  {
    const cross = Vector2.cross(data.v1, data.v2);
    if (0 < cross) return false;
  }
 
  return true;
}