電子書籍の厳選無料作品が豊富!

内積を用いた移動する線分と円の衝突判定について

作成しているプログラムはピンボールのフリッパーとボールとの衝突判定です。14歳からはじめるリアルに動くゲーム物理プログラミング教室C言語編と言う本を参考にプログラミングしています。左フリッパーが315度~360度、1度~45度の範囲を1度づつ移動する度に衝突判定をする関数を作成し、衝突した場合に関数から抜け、戻り値としてボールの情報を持つ構造体の中の反射スピードと反射座標を返すものです。

ここからが質問なのですが、ボールは全くフリッパーの衝突と関係の無い所で反射してしまいます。移動しない線分の両端の座標を与えた衝突判定(壁)では正しく判定するのですが、角度を与えて移動する線分(フリッパー)では上手くいきません。お分かりになる方いらっしゃいましたら、宜しくお願い致します。

関数に渡す引数はその時のボールの座標・速度などの構造体と左フリッパーの角度です。

以下ソースコード

//左フリッパーの軸と成る座標
float hx_l = (cx_l*ZOOM);//フリッパーのx座標
float hy_l = (cy_l*ZOOM+HORIZ);//フリッパーのy座標
//ベクトル a の成分を求める
float ax=xy.posx-hx_l;//線分始点からボール中心へのベクトル
float ay=xy.posy-hy_l;//線分始点からボール中心へのベクトル
//ベクトル b の成分を求める
float bx=((cx_l+cos(ToRadian(angler_l))*clubr)*ZOOM)-hx_l; //線分始点から終点へのベクトル
float by=((cy_l-sin(ToRadian(angler_l))*clubr)*ZOOM+HORIZ)-hy_l;//線分始点から終点へのベクトル
float inpro=ax*bx+ay*by;//ベクトル a,b の内積を求める
float bl=bx*bx+by*by;//ベクトル b の長さの二乗(線分始点から終点へのベクトル

if(inpro>-ZERO1 && inpro<(bl+ZERO1)){//フリッパーとの衝突判定
//ベクトル a の線分がボールの半径より小さければ交差している
if((ax*ax+ay*ay)-pow(inpro/sqrt(bl),2) < pow(xy.radius,2)){
以下判定後の処理‥‥

ベクトルbのbx,byを求める式でangler_lが引数として渡されるフリッパーの角度に成ります。ToRadian()は関数で角度をラジアンに変更します。

A 回答 (3件)

正しくは


if(inpro>-ZERO1 && inpro<(bl+ZERO1)){
if(((ax*ax+ay*ay)-pow(inpro/sqrt(bl),2) < pow(xy.radius,2)) && (cl < pow(xy.radius,2))){
...
}
}
ということでよろしいでしょうか。

いずれにしても、判定処理の内容がわからないのですが、それぞれの式は何を目的としているのでしょうか。
条件1、2がそれぞれフリッパーとボールがどのような位置関係にある場合にtrue,falseになるか把握されていますでしょうか。

ちなみに判例をもう1点提示しますと、

フリッパーの始点座標 = (0,0)
フリッパーの終点座標 = (1,0)
球の中心座標 = (2,1)
球の半径 = √2以上

の場合、フリッパーと球は交差しているはずですが、条件1の結果はどうなるでしょうか。


一般的な円対線分の交差判定としては下記どちらかのような内容になると思います。

1.
線分と円の中心の最短距離を求め、その最短距離が円の半径より小さければ交差している。

2.
線分の始点、終点のどちらかと円の中心との距離が円の半径より小さければ交差している。
そうでなければ、線分と円の輪郭の交点数を円と線分の方程式から求め、交点数が2個であれば交差している。交点数が1個であれば、円と線分は接している。
    • good
    • 0
この回答へのお礼

再び回答本当にありがとうございます。
参考文献に 「壁」 との衝突判定として以下の様(全く同じではないですが)なプログラムがあり

frametime はパソコンのリフレッシュレートにプログラムのタイミングを合わせる変数
GMS は重力加速度(9.80665)
BOUND_E は反発係数(-0.6)

float ax,ay,bx,by;//2つのベクトルの成分を求める

ax=ball.posx-line.sx;//線分始点から円中心へのベクトル
ay=ball.posy-line.sy;//線分始点から円中心へのベクトル
bx=line.ex-line.sx;//線分始点から終点へのベクトル
by=line.ey-line.sy;//線分始点から終点へのベクトル

float inpro=ax*bx+ay*by; //内積を求める
float bl=bx*bx+by*by;//直線(ベクトルB)の長さの二乗

if(inpro>-ZERO1 && inpro<(bl+ZERO1)){//lineとの衝突判定
if((ax*ax+ay*ay)-pow(inpro/sqrt(bl),2) < pow(xy.radius,2) ){ //距離を求める
float nx = by/sqrt(bl);//正規化された法線ベクトル
float ny = -bx/sqrt(bl);
float inpro2 = nx * -ball.speedx + ny * -ball.speedy;
nx *= inpro2;
ny *= inpro2;
ball.speedx = ((ball.speedx + GMS * frametime) + nx * 2) * BOUND_E;
ball.speedy = (ball.speedy + ny * 2) * BOUND_E;
ball.posx += ball.speedx * frametime * 3;
ball.posy += (ball.speedy * frametime + (GMS * frametime * frametime / 2)) * 3;
}
}

 上記のプログラムですと壁とボールは、ほぼ正しく衝突判定するので、単純にフリッパーの始点(軸になる)はそのままで、終点は三角関数を使用して角度とフリッパーの長さで終点を移動してそれぞれの角度とフリッパーの長さで求められる線分で衝突判定すれば良いのかと考えていました。
 そこで、線分の終点の部分をx軸の部分で言えば以下の様に書き換えてみたのでした。

clubr はフリッパーの長さ
ZOOM は画面の表示倍率

((cx_l+cos(ToRadian(angler_l))*clubr)*ZOOM)

 自分でもう一度教えて頂いた線と円との衝突判定を考慮しやり直してみます。御教授ありがとうございました。

お礼日時:2010/09/26 08:41

cl が何をさすのかわからんのだけど, 少なくとも元の判定はおかしい.


これは「フリッパーを延長した直線との距離」を考えているようにしか見えない. つまり, 「フリッパーとは衝突していないがフリッパーを延長した直線とは交差する」ような場合にも, 判定に成功するように見える.
とはいえ, これは「点と直線の距離」を使うのが自然だし, もっと簡単な式になると思うよ. あと, 2乗するのに pow を使うのははっきりいって無駄. 素直に同じものをかけ算した方がはるかに速い.
    • good
    • 0
この回答へのお礼

御回答ありがとうございます。

 確かに Tacosan のおっしゃる通り延長した直線と交差しているか判定し、その後距離を求める式になっています。
 もっと簡単に考えてプログラミングしてみます。
 それと、関数を使わずに単純に掛け算した方がスピード感を求められるゲームには良いですよね。

御意見本当にありがとうございました。

お礼日時:2010/09/26 07:11

条件式の意図が良くわからないのですが、わかりづらいのでベクトル積の形式で書くと



inpro = a・b
bl = |b|^2

=>

条件1 ( a・b > -ZERO1 && a・b < ( |b|^2 + ZERO1 ) )
条件2 ( |a|^2 - ( a・b / |b| )^2 < radius^2 )

となる。

たとえば a と b が平行で、 bの長さが1の場合を考えると、

条件1 ( |a| > -ZERO1 && |a| < ( 1 + ZERO1 ) )
条件2 ( |a|^2 - |a|^2 < radius^2 ) => ( 0 < radius^2 ) => true

ということで、上記条件の場合、( |a| > -ZERO1 && |a| < ( 1 + ZERO1 ) )
だけで交差が判定できるはず、という処理になっているんですが、球の半径がまったく考慮されないのは明らかにおかしいのではないでしょうか。

あと、ZERO1とは何でしょうか。FLT_MINですか?
もしそうであれば、実質的には( |a| < 1 )を見てるだけということになるんですが、、、

ひとまずgoogleで”線分と円”などで検索したらアルゴリズムが色々出てくるので調べてみてはいかがでしょうか。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
分かりづらく申し訳ありません。

参考文献によると ZERO1 は

const float ZERO1 = 1e-10;

となり、限りなくゼロに近い定数になります。
 ボールが壁にぶつかっても反射しなくなってしまうのを防ぐ為(実数計算の誤差により反発係数が0になってしまう)に「==0」や「>=0」の比較の変わりに「変数 < ZERO1」や「変数 > -ZERO1」の様に使う定数になります。
 それと、以下の部分で xy.radius がボールの半径になりこの行でボールの半径との交差判定をする様にしています。

//ベクトル a の線分が円の半径より小さければ交差している
if(((ax*ax+ay*ay)-pow(inpro/sqrt(bl),2) < pow(xy.radius,2)) && (cl < pow(xy.radius,2))){

 書き損じが多く分かり辛く申し訳ないのですが、よろしければ以上考慮のうえ再度御教授願えましたら幸いです。

お礼日時:2010/09/25 07:10

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


このQ&Aを見た人がよく見るQ&A