![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?8acaa2e)
内積を用いた移動する線分と円の衝突判定について
作成しているプログラムはピンボールのフリッパーとボールとの衝突判定です。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()は関数で角度をラジアンに変更します。
No.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個であれば、円と線分は接している。
再び回答本当にありがとうございます。
参考文献に 「壁」 との衝突判定として以下の様(全く同じではないですが)なプログラムがあり
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)
自分でもう一度教えて頂いた線と円との衝突判定を考慮しやり直してみます。御教授ありがとうございました。
No.2
- 回答日時:
cl が何をさすのかわからんのだけど, 少なくとも元の判定はおかしい.
これは「フリッパーを延長した直線との距離」を考えているようにしか見えない. つまり, 「フリッパーとは衝突していないがフリッパーを延長した直線とは交差する」ような場合にも, 判定に成功するように見える.
とはいえ, これは「点と直線の距離」を使うのが自然だし, もっと簡単な式になると思うよ. あと, 2乗するのに pow を使うのははっきりいって無駄. 素直に同じものをかけ算した方がはるかに速い.
御回答ありがとうございます。
確かに Tacosan のおっしゃる通り延長した直線と交差しているか判定し、その後距離を求める式になっています。
もっと簡単に考えてプログラミングしてみます。
それと、関数を使わずに単純に掛け算した方がスピード感を求められるゲームには良いですよね。
御意見本当にありがとうございました。
No.1
- 回答日時:
条件式の意図が良くわからないのですが、わかりづらいのでベクトル積の形式で書くと
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))){
書き損じが多く分かり辛く申し訳ないのですが、よろしければ以上考慮のうえ再度御教授願えましたら幸いです。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 物理学 ベクトルと座標系につきまして 1 2022/04/03 06:23
- 物理学 高1力学の運動量の問題です。問題を一通り解いたのですが、行き詰まってしまったのでご回答頂ければ嬉しい 3 2022/06/29 11:20
- 数学 ベクトル方程式(ヘッセの標準形)についての質問 2 2022/04/23 18:00
- 数学 球面と接する直線の軌跡が表す領域 4 2023/07/30 12:37
- 物理学 角運動量の式変形が分かりません。 4 2022/08/03 21:04
- 物理学 xy平面上を運動する物体Aがある。この物体の時刻tにおける位置ベクトルra(t)がra(t)=p + 2 2022/05/22 14:00
- 物理学 角速度ベクトルにつきまして 3 2022/08/09 15:44
- 物理学 面積速度一定の法則を(1/2)r v sinθを使って証明する方法 2 2023/06/25 12:43
- 物理学 xy平面上を運動する物体Aがある。この物体の時刻tにおける位置ベクトルra(t)がra(t)=p + 1 2022/05/23 21:39
- 物理学 なめらかな水平面の床の上に、質量 200 g の物体がある。床の面を xy 面とし、鉛直方向に z 1 2022/07/23 11:28
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
グラフの交点の求め方(Excel)
-
マインクラフト(pc版)で座標...
-
3次元空間上の2つの座標から...
-
VB.net フォームをなめらかに移...
-
C言語の書き方
-
当たり判定後に面に沿って動か...
-
C言語で制作するピラミッドアー...
-
直線上にある点の座標の求め方
-
日本国内の、アクセス地域によ...
-
エクセルである点からの距離で...
-
EXCEL VBA でウインドウの...
-
StretchDIBits
-
求積表の計算方法
-
ピッキングのお仕事ってきつい...
-
y=x^2の座標をプロットするプロ...
-
MFC
-
ガウシアンフィルタのCプログラム
-
OpenCvSharp4による画像判定解...
-
c#で直線が図形内にあるか判定
-
C言語において、0の座標をキャ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
グラフの交点の求め方(Excel)
-
マインクラフト(pc版)で座標...
-
3次元空間上の2つの座標から...
-
エクセルである点からの距離で...
-
始点、終点の二つの座標と半径...
-
エクセルで回転する座標の出し方
-
閉図形の座標の配列が右回りか...
-
ワード上Shapeの位置情報を統一...
-
以下のプログラムは重心を求め...
-
C言語 配列で座標
-
ダイアログ内コントロールの位...
-
ガウシアンフィルタのCプログラム
-
空間上の二点を結ぶ直線上に任...
-
エクセルシート上のマウスポイ...
-
シーケンサー(PLC?)で制...
-
VB6のPrinter.ScaleWidth に対...
-
多角形の内部かどうか判定する方法
-
直線上にある点の座標の求め方
-
y=x^2の座標をプロットするプロ...
-
ピクチャボックスの座標取得
おすすめ情報