トップページ > 過去ログ > 記事閲覧
3D 壁との当たり判定について
名前:顔芸 日時: 2012/08/28 18:33

はじめまして。 今、DXライブラリとc++で3Dゲームを制作しているのですが、移動させるモデルと壁との当たり判定について行き詰ってしまったので質問させて頂きます。 早速。。。 ちょうど移動モデルの真上から見たときに  ○ ●□ 黒丸の位置から白丸の位置へ移動ベクトル(斜めの)を向けて壁沿いに移動しようとする時、黒丸側の壁面のみについて、法線方向の速度成分を0にして壁に沿わせればよいのでしょうが、白丸側の壁面も同時に当たり判定に加味されてしまうことによって、そちらの法線方向成分も0になり、結果として黒丸はその場に立ち尽くしてしまいます。 この問題に関連してか、上記のような直角の角をもつ壁面以外の壁面でも、ポリゴンのつなぎ目での移動モデルの挙動がおかしなものになってしまいます。 当方、「サンプルプログラム 3Dアクション基本」のソースを参考にさせて頂いているのですが、該当の当たり判定の部分を見ても、実行してみてどうしてこの問題が発生しないのか見当がつきません。 サンプルでは具体的にどの部分の工夫によってこの問題を解消できているのでしょうか。 ご回答よろしくお願い致しますm(_ _)m

Page: 1 |

Re: 3D 壁との当たり判定について ( No.1 )
名前:いっち 日時:2012/08/29 19:41

> サンプルでは具体的にどの部分の工夫によってこの問題を解消できているのでしょうか。 関数 Player_Move のおよそ 89行目〜132行目のループ部分です。 特に重要なのは以下の部分です。 100> // 壁に当たったら壁に遮られない移動成分分だけ移動する 101> { 102> VECTOR SlideVec ; // プレイヤーをスライドさせるベクトル 103> 104> // 進行方向ベクトルと壁ポリゴンの法線ベクトルに垂直なベクトルを算出 105> SlideVec = VCross( MoveVector, Poly->Normal ) ; 106> 107> // 算出したベクトルと壁ポリゴンの法線ベクトルに垂直なベクトルを算出、これが 108> // 元の移動成分から壁方向の移動成分を抜いたベクトル 109> SlideVec = VCross( Poly->Normal, SlideVec ) ; 110> 111> // それを移動前の座標に足したものを新たな座標とする 112> NowPos = VAdd( OldPos, SlideVec ) ; 113> } > 白丸側の壁面も同時に当たり判定に加味されてしまうこと このループでは複数の壁ポリゴンに対して当たり判定を取っていますが、基準は常に OldPos と MoveVector なので、 補正済みの移動量を表すベクトル(SlideVec)が他の壁ポリゴンの判定結果に影響することはありません。 (NowPosが更新されてループの次週で参照されるので全く無いというわけではないですが)
Re: 3D 壁との当たり判定について ( No.2 )
名前:顔芸 日時:2012/08/30 00:32

ご回答ありがとうございますm(_ _)m この件に関して今日は1日中頭を悩ましておりました^^; たしかにSlideVec周辺かなぁとも思ったのですが、 結論から先に申し上げますと、SlideVecは結局壁沿いの移動を滑らかにする役割を担っているだけで、結局重要なのはその後の「押し出し」ではないのか・・?ということです。 示していただいたSlideVec周辺のソースですが、直後の // 新たな移動座標で壁ポリゴンと当たっていないかどうかを判定する for( j = 0 ; j < KabeNum ; j ++ ) { // j番目の壁ポリゴンのアドレスを壁ポリゴンポインタ配列から取得 Poly = Kabe[ j ] ; // 当たっていたらループから抜ける if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) break ; } // j が KabeNum だった場合はどのポリゴンとも当たらなかったということなので // 壁に当たったフラグを倒した上でループから抜ける if( j == KabeNum ) { HitFlag = 0 ; break ; } という部分を見る限り、結局のところ、 修正しても衝突が回避できなかった場合はのちの押し出し処理にゆだねるしかない・・ という風に解釈できる気がしました。そこで、ためしに437行辺りの // 移動したかどうかで処理を分岐 if( MoveFlag == 1 ) から505行辺りの // 壁に当たっていたら壁から押し出す処理を行う if( HitFlag == 1 ) までにある当たり判定をごっそり消去して、はなから押し出しのみに頼るようなソースにしたところ、 よくも悪くも、質問文の事象を含め、ほとんど問題なく機能しました。このことから、あくまで自分の予想なのですが、 質問文の黒丸から白丸に一旦移動して、そこでめり込んでいる2つの壁の法線方向に押し出すことで ◎○ ●□ 結局◎の位置に落ち着くのでは・・・?という結論に至りました。 しかし、「ほとんど」と申し上げたのは、見つかった限りでは、 @壁沿いの移動が若干ジグザグしている気がする Axyz3方向を床と壁に囲われた(サンプルでは壁沿いの立方体の足場の始点など)頂点に向かってジャンプをし続けると壁抜けして奈落の底に落ちてゆくことがある というような問題があったからです。 これらの問題との関連も調べつつ気長に考えていきたいと思いますが、以上の点に関してなにかご意見がありましたらぜひともお聞かせください。よろしくお願いします。
Re: 3D 壁との当たり判定について ( No.3 )
名前:いっち 日時:2012/09/01 13:08

> @壁沿いの移動が若干ジグザグしている気がする > Axyz3方向を床と壁に囲われた(サンプルでは壁沿いの立方体の足場の始点など)頂点に向かってジャンプをし続けると壁抜けして奈落の底に落ちてゆくことがある 実際の事象を見てみないとわかりません。 Aは「サンプルプログラム 3Dアクション基本」でも発生するのでしょうか?
Re: 3D 壁との当たり判定について ( No.4 )
名前:顔芸 日時:2012/09/01 20:18

ご回答ありがとうございます。 はい、サンプルの方で発生を確認しました。(た だ、@、Aが発生したのは壁の当たり判定を削除 し、押し出しのみにした場合の話であり、完成版の 不良ではありません。念のため。。。) 結局自分はなにがしたかったかといいますと、 「slidevecに変換しても壁抜けが防げない時は押し 出しに任せよう」という消極的なニュアンス(言葉が 悪いかもしれません すみません^^;)でslidevecと押 し出しの両方を実装しているのなら、なんとかして どちらか一方に絞る道はないだろうかと模索していたのです。 例えばサンプルでは、移動ベクトルをslidevecに変 換したあと、1度はループで再衝突を確認していま したが、再々衝突、再々々・・・まではチェックし ていないようだったので、ベクトル変換・衝突確認 の一連の処理を再帰関数化し、「確実に」衝突を回 避するslidevecを作ろう と試みました。 しかし結果的には失敗でした。 鋭角の角地に向かって歩くようにモデルを動かせる と、必ずどちらかの壁に平行に、ヌルーっと壁抜け してしまいました。 この時の移動ベクトルとslidevec(再帰なので何度も 変換します)の遷移を実際に紙に書き出してみたので すが、角地を作る2枚の壁をA,Bとすると、Aに対す る垂直方向成分を0にしたあと、残ったベクトルか ら今度はBについて垂直方向成分を0にするわけです が、そうすると、残ったベクトルは再びAへの移動成 分を帯びてしまいました。 つまり「1度評価した壁にめり込むことはない」と は言えないようです。↑これを前提に再帰関数を組 んでいたので当然破綻しました(_ _) このあたりからもう心が折れて、素直に両方実装し ましたがなんだか歯がゆいです^^; やっぱり一般には @移動ベクトルの評価 A衝突があれば壁への垂直成分を0にする Bそれでも衝突があれば押し出し という流れになるんですかね・・?もうそう思って 納得した方がいいのかなぁ; 一応使用した再帰関数を載せておきます。 色々と荒いですが; 1 : VECTOR CGame::AvoidWallCore(int start, CGround ground, CObject object)//確定移動ベクトルを返す再帰関数 2 : { 3 : VECTOR newpos = object.GetPos() + object.GetMoveVec(); 4 : MV1_COLL_RESULT_POLY_DIM colsphere = MV1CollCheck_Sphere(ground.GetHandle(), -1, newpos, object.GetCapture()); 5 : MV1_COLL_RESULT_POLY *hitpoly = colsphere.Dim; 6 : 7 : for (int k=start; k<colsphere.HitNum; k++)//カプセルの当たり判定 8 : { 9 : if (!IsWall(hitpoly[k], object.GetPos().y)) 10 : continue;//壁以外はスルー 11 : 12 : if (!HitCheck_Capsule_Triangle( 13 : newpos, 14 : VGet(newpos.x, newpos.y + object.GetHeight(), newpos.z), 15 : object.GetRadius(), 16 : hitpoly[k].Position[0], 17 : hitpoly[k].Position[1], 18 : hitpoly[k].Position[2] 19 : )) 20 : continue;//当たっていなければスルー @k-1番目まで衝突がなく、k番目のポリゴンと衝突した時、 21 : 22 : { 23 : VECTOR newmovevec; 24 : 25 : newmovevec = VCross(object.GetMoveVec(), hitpoly[k].Normal); 26 : newmovevec = VCross(hitpoly[k].Normal, newmovevec); 27 : object.SetMoveVec(newmovevec);//Ak番目の壁ポリゴンとは接触しない移動ベクトルを作り、 28 : } 29 : 30 : MV1CollResultPolyDimTerminate(colsphere); 31 : return AvoidWallCore(k+1, ground, object);//Bk+1番目以降のポリゴンとも衝突しないベクトルを返せばok 32 : } 33 : 34 : MV1CollResultPolyDimTerminate(colsphere); 35 : return object.GetMoveVec(); 36 : }
Re: 3D 壁との当たり判定について ( No.5 )
名前:管理人 日時:2012/09/02 05:33

私も詳しくは分かりませんが、物理学的に正しい挙動を再現するエンジンなども存在しますので、 実際市販されている3Dゲームの当たり判定処理はサンプルプログラムにあるような当たり判定処理よりも複雑で、 且つすり抜けなどが発生しないような処理を行っていると思います
Re: 3D 壁との当たり判定について ( No.6 )
名前:顔芸 日時:2012/09/02 10:12

ご回答ありがとうございます。 ちょっとした疑問から横道に逸れてしまいましたが、サンプルの当たり判定は自分のプログラムにとって 十二分の完成度ですので、しっかり参考にさせて頂きました。ありがとうございましたm(_ _)m 別の手法などもまた個人的に調べてみようと思います。 いっちさんもお付き合いありがとうございましたm(_ _)m (解決です)
Re: 3D 壁との当たり判定について ( No.7 )
名前:いっち 日時:2012/09/02 16:31

解決ということなので今更なのですが、(No.4)のソースにおいて > object.SetMoveVec(newmovevec);//Ak番目の壁ポリゴンとは接触しない移動ベクトルを作り、 と、ありますが以下の様な図の状況で ○壁壁壁壁@ 壁 壁 A '○'がキャラだとしてキャラを右(壁@)方向に移動させようとした場合、 壁@にキャラが埋まるようなベクトルが作られる可能性については考慮されているのでしょうか? ※追記 それと MV1_COLL_RESULT_POLY_DIM を関数を呼ぶごとに取り直しているようですが、 そうすると start の値は全く意味がいないように思います。
Re: 3D 壁との当たり判定について ( No.8 )
名前:顔芸 日時:2012/09/02 16:47

あ、どうも(^^ゝ この再帰関数は結局不採用で書き進めてしまったのですぐに実行・評価することができないのですが、 ○壁壁壁壁@ 壁 壁 A この位置ではまだ壁Aの存在が効いてますので壁@への移動成分は0になると思いますが。。。 ○  壁壁壁壁@ 壁 壁 A 右に進めるようになるのはこの位置からかと。。。 【追記について】 球判定を取り直しているのは新たな暫定移動先を中心に判定をするためであり、「一度当たり判定をした壁には二度と衝突しない」という仮定のもとに、 無駄を省く意味で設置したstartとは基本的に無関係だと思うのですが。実際どうなんでしょうか;
Re: 3D 壁との当たり判定について ( No.9 )
名前:いっち 日時:2012/09/02 22:14

その関数でどのような結果が得られることを期待しているのかわかりかねるのですが、 > 球判定を取り直しているのは新たな暫定移動先を中心に判定をするためであり、「一度当たり判定をした壁には二度と衝突しない」という仮定のもとに、 > 無駄を省く意味で設置したstartとは基本的に無関係だと思うのですが。実際どうなんでしょうか; MV1CollCheck_Sphere で得られる結果が毎回、同じポリゴンで同じ順番で同じ数でない限り start の値は無意味だと思います。 (毎回同じパラメータで MV1CollCheck_Sphere を呼べばおそらく同じ結果が得られると思いますが、順番についても保証されるのか私には分かりません) > この位置ではまだ壁Aの存在が効いてますので壁@への移動成分は0になると思いますが。。。 上記の件も合わせて考えると、壁Aよりも壁@の評価が先に行われた場合必ずしもそうとは言い切れないのではないでしょうか。 また、これは、例えば右斜め下方向への移動ベクトルが与えられた場合、結果として真下方向への移動ベクトルが欲しいにもかかわらず、 移動成分が0になってしまう可能性があるという事だと思います。
Re: 3D 壁との当たり判定について ( No.10 )
名前:顔芸 日時:2012/09/02 23:03

>MV1CollCheck_Sphere で得られる結果が毎回、同じポリゴンで同じ順番で同じ数でない限り start の値は無意味だと思います。 >壁Aよりも壁@の評価が先に行われた場合必ずしもそうとは言い切れないのではないでしょうか。 それはその通りですが。。。なんだか話をややこしくしてしまい申し訳ないのですが、要するに最初にレスして頂いた 「このループでは複数の壁ポリゴンに対して当たり判定を取っていますが、基準は常に OldPos と MoveVector なので、 補正済みの移動量を表すベクトル(SlideVec)が他の壁ポリゴンの判定結果に影響することはありません。」 という部分が、調べてみたところどうやら怪しいという結論を示したつもりだったのですが。 補正済みの移動量を表すベクトルが他の壁ポリゴンの判定結果に影響することを認めるならば、 それはもちろん壁の評価順なども問題になってくるでしょう。実際、どうやらその通りだったので 上記の再帰関数を放棄した という運びです。 ですからその関数のことは忘れてやって下さい^^;

Page: 1 |