1. 無職の学び舎
  2. >
  3. 無職はゲーム数学の勉強をする
  4. >
  5. 点と線分の衝突

点と線分の衝突

点と線分の当たり判定は、点と半直線の衝突と似ていてベクトルの内積を使って当たりを判定します。

ただ、線分には長さがあるので内積だけでは判定できず、もうひと手間必要になってきます。

相変わらずですが、小数点の細かい差異のおかげで滑らかな動きにするとほとんどあたりません。

前提知識

考え方

$ \vec{a} $:線分の向きと長さを表すベクトル
$ \vec{b} $:線分の始点から $点P$ に向かうベクトル

$点P$ が線分上にあるとき

$ \vec{a} $ と $ \vec{b} $ は同じ向きで平行になります。

これは点と線分が当たっているかどうかの条件の1つになります。

しかし、線分には長さがあるので、$ \vec{a} $ と $ \vec{b} $ が同じ向きで平行だったとしても、まだ当たっているかどうかはわかりません。

このケースは $ \vec{a} $ と $ \vec{b} $ は同じ向きで平行ですが $点P$ と線分は当たっていません。

$点P$ が線分の上にあるかどうかを判定する必要があります。

もし、$点P$ が線分上にあるのであれば、 $\vec{b}$ の長さは $\vec{a}$ の長さより短くなっていなければならないはずです。

よって、$\vec{b}$ と $\vec{a}$ の長さを比較すれば線分上にあるかどうかを判定する事ができます。

点と線分の衝突判定の方法をまとめると

$ \vec{a} $ と $ \vec{b} $ が同じ向きで平行、かつ $|\vec{a}| > |\vec{b}| $

という事になります。

$ \vec{a} $ と $ \vec{b} $ が同じ向きで平行かどうか

$ \vec{a} $ と $ \vec{b} $ が同じ向きで平行かどうかは以下の式で判定します。

$ \vec{a} $ と $ \vec{b} $ が同じ向きで平行なとき
$ \vec{a} \cdot \vec{b} = |\vec{a}| |\vec{b}| $ が成り立ちます。

こちらに関してはベクトルの内積の特性点と半直線の衝突で解説しています。

サンプルコード

/**
  * 点と線分が当たっているかどうか
  * @param p 点
  * @param segt 線分
  */
 function intercect(p:Vector2, seg: Segment)
 {
   // 線分の方向と長さを表すベクトルをa
   const a = seg.v;

   // 線分の始点から点に向かうベクトルをbとする
   const b = Vector2.sub(p, seg.p1);
 
   // ベクトルaの長さをal、ベクトルbの長さをblとする
   const al = a.magnitude;
   const bl = b.magnitude;
 
   // a と b の長さを掛け合わせたものを ab
   const ab = al * bl;

   // a と b の内積を dot
   const dot = Vector2.dot(a, b);

   // p が線分上にあるとしたら、a と b は同じ向きで平行なはずなので
   // dot = ab になっているはず、なっていなければ当たっていない
   if (dot !== ab) {
    return false;
   } 
 
   // bの長さが a の長さより短ければ当たっているといえる
   return (al > bl);
 }