円と線分の当たり判定を行うには

◆PROCESSING 逆引きリファレンス

 カテゴリー:ゲーム作成

円と線分の当たり判定を行うには

【解説】

多くのゲームでは、物と物が衝突したかどうかを判定する処理(当たり判定)は定番ですね。

ゲーム専用のフレームワーク(UnityやCocos2d-X)では、専用の当たり判定命令が用意されています。

残念ながらPROCESSINGには、当たり判定を行う便利な標準命令はありません。

矩形同士、円同士、多角形同士の当たり判定については下記のページで解説しました。

ここでは斜めになっている四角形と円の当たりを判定を考えてみます。

四角形と円の当たりを判定は、例えば四角形のミサイルと円状のUFOや、ボール反射ゲームの反射バーと球の当たり判定などで利用できそうですね。

円と四角
(画像URL:deviantart.com 様、GAHAG | 著作権フリー写真・イラスト素材集 様:著作者(Free Download Web さん/ID:201411190600)、pixabay 様:OpenClipart-Vectorsさん)

なんだか難しそうですが、要は四角形の辺を構成している線分と円の中心点を用いて、「線分と中心点の最短距離」を求め、その距離が円の半径よりも大きいか小さいかを判定すればOKです。

点と線分
上図の場合、青い四角形と円があたっているかどうかは、四角形の辺ABに向かって円の中心点Pから引いた垂線PXの長さが、半径rよりも大きいか小さいかを判定すれば良いことがわかります。

ある点Pから線分ABへ下ろした垂線PXの長さを知るには、ベクトル演算の外積を使います。

また点Xの座標を知るにはAXの長さを得る必要がありますが、この時はベクトル演算の内積を使います。

もう・・・ベクトル演算なんてとうの昔に忘れ去りましたので・・・いまさらオジサマに内積と外積の説明はできません(汗)が・・・単純に書くと下図のような関係になります。

●AXの長さ(内積を使う)

内積
ベクトルAPの長さ(|AP|)×コサインθが内積(ベクトルAXの長さ)です。

●PXの長さ(外積を使う)

外積

ベクトルAPの長さ(|AP|)×サインθが外積(ベクトルPXの長さ)です。

サイン・コサインを使って計算しても良いのですが、点A、B、Pの座標がわかっているのであれば、単純な掛け算で計算可能です。

上図の場合、内積の式は以下の様に変換できます。
内積式
ip は「内積(inner product)」の事です。ここでのポイントはベクトルABの長さ(|AB|)を1と見立てて、計算を単純化している事です。

また外積の式は以下の様に変換できます。
外積式
cpは「外積(cross product)」の事です。

このように長さを1に見立てたベクトルの事を「単位ベクトル」と呼び、あるベクトルを単位ベクトルに変換する事を「正規化(ノーマライズ)」と呼びます。

最終的にプログラムで計算する際には、ベクトルABを正規化したものをvAB、ベクトルAPをvAPとすると、以下の計算式で求めることが可能です。

内積外積計算式

※上記記事は、deq notes 様、2D当たり判定超入門 様、ゲームプログラミング技術集 様を参考とさせて頂きました。ありがとうございます。

線分と円の当たり判定を考える際、もう1つ考慮しなければいけない事があります。それは線分の範囲内に円の中心があるか否かという事です。

例えば下図の場合
円が範囲外1
点 P’ はベクトルABの外(点Aよりも左)にあります。この場合、点P’ と線分ABの最短距離はX’P’  ではなくてAP’  になります。点Pが線分AB上にあれば、XP が最短距離です。

それでは点Pが線分ABから見てどこにあるのかを、どうやって判定すれば良いでしょうか?。

これにはベクトルABとベクトルAPが作る角度(θ)のコサイン値が正負のどちらであるかを判定すれば良いと、いろいろなサイト様に解説されています。
円が範囲外1判定1
これは簡単にいえば、点Aから見て点Xの方向が右か左か(AXの長さが正か負か)を判断すれば良いという事です。
円が範囲外1判定2AXの方向(長さ)については、先に説明した内積の計算で求めることが可能です。

また以下の様なケースもあります。
円が範囲外2
点P’ が点Bよりも右側にある場合です。この場合も線分ABと点P’の最短距離は、BP’となります。
円が範囲外2判定
この場合は、ABの長さ(a)を内積計算で求めて、APの長さ(b)と比べてどちらが長い(大きい)かを判断すれば良い事になります。bの方が長ければ、点Pは点Bの右側にあります。

※上記はGihyo.jp 様:ActionScript 3.0で始めるオブジェクト指向スクリプティング記事を参考とさせて頂きました。ありがとうございます。

下図のような三角形APXにおいて
三平方の定理
ベクトルAPの長さ(辺cの長さ)は三平方の定理により計算可能ですが、PROCESSINGには2点間の長さを計算してくれる便利な命令 dist() があります。

今回はこれを使ってみたいと思います。

 

【構文】

float  2df  =  dist(  float  x1,  float  y1,  float  x2,  float  y2  ) ;
float  3df  =  dist(  float  x1,  float  y1,  float  z1,  float  x2,   flost  y2,  float  z2 ) ;

【パラメータ】

2df  :  計算された (  x1,  y1  )  –  (  x2,  y2  )  の距離
3df  :  計算された (  x1,  y1,  z1  )  –  (  x2,  y2,  z2  )  の距離
x1、y1、z1 : 1つ目の座標
x2、y2、z2 : 2つ目の座標

Zを用いる計算は size() で P3D の3D系座標を指定している場合に限ります。

 

【関連記事】


サンプルプログラム

2点間の距離を計算する例:

上記プログラムを実行すると青点(A)と緑点(B)を結ぶ直線が描かれます。また画面左上にAB間の長さが表示されます。

マウスで点Aまたは点Bをドラッグして動かすと、距離表示が変わることがわかると思います。

<出力サンプル>
線分と円Sample1

 

円と線分の衝突判定例:

上記プログラムを実行すると青点(A)と緑点(B)を結ぶ直線が描かれます。また画面中央に円が描かれます。

マウスで青点または緑点をドラッグし、線分が円と重なると、円が赤くなります。

Point2Dは点の座標を管理するクラス、Vector2Dはベクトルを管理するクラスです。
Vector2Dクラスには、本文で説明した正規化、内積、外積を計算するメソッドが備わっています。

正規化を計算する都合上、(無理やり)オーバロードコンストラクタを使っていますが、線分と円の当たり判定には直接関係ありません。

オーバロードコンストラクタの利用は、私の力量不足ですね・・・。この辺りは、もう少しスマートな記述があるかもしれません(汗)。

<出力サンプル>
線分と円Sample2

下記はサンプルプログラムをP5.jsで書き直したものです。マウス(またはタッチ操作)で、青い点か緑の点をドラッグしてください。動作イメージを確認できます。

 


PROCESSING逆引きリファレンス一覧 へ戻る

本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。