- ベストアンサー
内積を用いた移動する線分と円の衝突判定について
- プログラムはピンボールのフリッパーとボールとの衝突判定
- フリッパーの移動範囲で衝突判定をする関数を作成
- 移動する線分(フリッパー)では正しく判定しない問題
- みんなの回答 (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個であれば、円と線分は接している。
その他の回答 (2)
- Tacosan
- ベストアンサー率23% (3656/15482)
cl が何をさすのかわからんのだけど, 少なくとも元の判定はおかしい. これは「フリッパーを延長した直線との距離」を考えているようにしか見えない. つまり, 「フリッパーとは衝突していないがフリッパーを延長した直線とは交差する」ような場合にも, 判定に成功するように見える. とはいえ, これは「点と直線の距離」を使うのが自然だし, もっと簡単な式になると思うよ. あと, 2乗するのに pow を使うのははっきりいって無駄. 素直に同じものをかけ算した方がはるかに速い.
お礼
御回答ありがとうございます。 確かに Tacosan のおっしゃる通り延長した直線と交差しているか判定し、その後距離を求める式になっています。 もっと簡単に考えてプログラミングしてみます。 それと、関数を使わずに単純に掛け算した方がスピード感を求められるゲームには良いですよね。 御意見本当にありがとうございました。
- qwertfk
- ベストアンサー率67% (55/81)
条件式の意図が良くわからないのですが、わかりづらいのでベクトル積の形式で書くと 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で”線分と円”などで検索したらアルゴリズムが色々出てくるので調べてみてはいかがでしょうか。
お礼
回答ありがとうございます。 分かりづらく申し訳ありません。 参考文献によると 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))){ 書き損じが多く分かり辛く申し訳ないのですが、よろしければ以上考慮のうえ再度御教授願えましたら幸いです。
お礼
再び回答本当にありがとうございます。 参考文献に 「壁」 との衝突判定として以下の様(全く同じではないですが)なプログラムがあり 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) 自分でもう一度教えて頂いた線と円との衝突判定を考慮しやり直してみます。御教授ありがとうございました。