線分と円の接触判定

nil
記事: 428
登録日時: 14年前

線分と円の接触判定

投稿記事 by nil » 13年前

そういえばどうすれば接触判定できるんだろうな~、と授業中気になり、
部活の時間中にプログラムを組んでいたらどうにかうまいことできたので書いておこうかと思います。

線分 円 当たり判定 とかで調べれば結構該当する情報はヒットするんですが、
ベクトルをまだ習ってないのであんまり理解できなかったんですよね。
まぁ習っていないから分からない、はただの甘えですけどね……これからちゃんと調べて自主的に勉強しよう……

まず考えたのは直線と点の距離です。
[album]523[/album]
距離はこの図の通りで
θ=atan2( py-y1, px-x1 ) - atan2( y2-y1, x2-x1 )
r=sqrt( (px-x1)*(px-x1) + (py-y1)*(py-y1) )
[追記]画像では距離に絶対値記号を付け忘れました。正しくは|r*sinθ|でした。
です。
これはおk

次は場合分けです。

線分と円が接触している場合、次の2通りが考えられます。
すなわち
1.線分の始点(終点)と円が接触している。
2.線分と円が2点で交わっている。
[album]524[/album]
です。

1.は単純に円と点の当たり判定なので省略。

2.であるかどうかの判定をどうすればいいのか少し考えた結果、
以下の図の印をつけた角が双方とも鋭角もしくは直角であり、かつ中心点と線分の距離が円の半径r以下である
場合2.が成立すると気付いたので、これを採用。
[album]525[/album]
角をそれぞれθ1, θ2とした時に双方とも鋭角ならば
cosθ1 >= 0, cosθ2 >= 0の両方が真である。

よって
接触判定のコードは

CODE:

/*
*	@brief	線分と円の接触判定
*	@param	x1, y1, x2, y2	線分の始点と終点の座標
*			cx, cy			円の中心点の座標
*			r				円の半径
*	@return	判定結果
*/
bool CheckHitLineToCircle( float x1, float y1, float x2, float y2, float cx, float cy, float r ){
	float r1 = sqrt( (cx-x1)*(cx-x1) + (cy-y1)*(cy-y1) );	//	(x1,y1)と(cx,cy)の距離
	float r2 = sqrt( (cx-x2)*(cx-x2) + (cy-y2)*(cy-y2) );	//	(x2,y2)と(cx,cy)の距離

	float t1 = atan2( cy-y1, cx-x1 ) - atan2( y2-y1, x2-x1 );	//	(x2,y2)-(x1,y1)-(cx,cy)の成す角
	float t2 = atan2( cy-y2, cx-x2 ) - atan2( y1-y2, x1-x2 );	//	(x1,y1)-(x2,y2)-(cx,cy)の成す角
	
	float x = fabs( sin(t1) * r1 );	//	(x1,y1),(x2,y2)を通る直線と円の中心点の距離
	
	return ( r1  0 && cos(t2) > 0 && x < r );
}
となりました。

……前回までの日記で作っていたゲームは一旦設計を見直しつつモリモリと作りなおしてます。
最後に編集したユーザー nil on 2012年11月21日(水) 20:13 [ 編集 2 回目 ]

アバター
LisetteLander
記事: 147
登録日時: 14年前

Re: 線分と円の接触判定

投稿記事 by LisetteLander » 13年前

ゲームで使うならsqrtはやめたほうがいいらしいです^^;
なんでも処理がすごい重いらしく、その数値の用途が比較だけなら計算式を比較対象したほうがいいと…

nil
記事: 428
登録日時: 14年前

Re: 線分と円の接触判定

投稿記事 by nil » 13年前

ご指摘ありがとうございます!!
書き直してIdeoneで速度比較をしてみました。
► スポイラーを表示
引数に適当な値を代入して30000000回呼び出しました。

CODE:

sqrt使用
14.06s
13.87s
13.90s
修正後
13.15s
13.67s
13.64s
目に見えてってほどではありませんが、結構な短縮になりました!

アバター
tk-xleader
記事: 158
登録日時: 14年前

RE: 線分と円の接触判定

投稿記事 by tk-xleader » 13年前

 今回の場合、で定義されている関数を一切用いずに判定することが出来ます。特に(2)の場合について、三角関数を使わないで判定する方法の基礎となる考え方を図付きの画像で示してみました。

画像

 鉛筆で書いたので、ちょっと薄いですがごめんなさい。
最後に編集したユーザー tk-xleader on 2012年11月23日(金) 02:08 [ 編集 2 回目 ]

nil
記事: 428
登録日時: 14年前

Re: 線分と円の接触判定

投稿記事 by nil » 13年前

ご丁寧にありがとうございますm(_ _)m
なるほど、直線を一次方程式と見て処理を行うのですね。
参考にコードを書き起こしてみようと思います!!