Bulletでゲームを作ってみるPart3 キャラの移動

前回解説したBulletのコリジョン判定処理を使ってキャラの移動処理を作成します。複雑な状況でも正確で安定したコリジョン判定と移動がストレスにならない良好な操作性が目標です。
判定処理の検証や実験でいくつかキャラ移動処理を試作したので、キャラ移動処理テクニックと合わせて解説していきます。最終的な移動処理は、かなり複雑になってしまったため詳細は省略します。サンプルソースコードにできるだけ解説、問題と解決法など詳しくコメントしているので参考にしてください。



キャラ移動処理

コリジョンに衝突するなら移動しないという方法を最初に思いつきましたが、それでは壁などに引っかかって思うように動かせず、かなり操作性が悪くなります。なので、正確なコリジョン判定と同時に操作性を良くする方法を考える必要があります。


Discrete(離散)とContinuous(連続)コリジョン判定

図のようにDiscrete(離散)は、移動途中は無視して移動後にコリジョンに衝突したか判定します。処理は簡単ですが、移動量が大きいと薄い壁などを通り抜けてしまう問題(トンネリング)があります。トンネリングを起こさないようにするには時間間隔を短くするか、図右のように連続移動させて衝突判定するContinuous(連続)コリジョン判定を行う必要があります。ただし複雑な計算が必要になるため計算負荷が高い方法です。状況や計算コストなどを考慮して使い分ける必要があります。


特殊な移動 垂直方向テスト

キャラを横方向(XZ軸)に移動させ、キャラ上空(Y軸)からSweepTestまたはrayTestを行い、衝突した場所にキャラを立たせます。横方向のコリジョンを無視したデバッグ移動、NPCの接地、オブジェクト(草、木など)を地面に配置などゲーム本体ではなく制作ツールなどにも使用できます。


Discrete(離散)コリジョン判定にによるキャラ移動

まずは単純に移動させてから接触判定(ContactTestなど)を行い、侵入した距離だけ外へ押し出します。水平の床、垂直な壁のような単純なコリジョンや移動速度が遅ければ、この方法で対応可能です。キャラ(人間)が走る速度程度であればトンネリングも問題になることはないと思います(移動距離 < キャラの幅)。単純な方法なので計算負荷が軽く、さらに侵入した距離だけ外へ押し出す処理を行うことで壁に引っかからずに滑らかに移動できて操作性も良好です。ただし、複雑な形状は対応できません。特に角度の浅いハの字やV字型のコリジョンが苦手です。


コリジョン侵入解消処理

ContactTestなどの接触判定で、侵入した距離と方向(法線)がわかるので、侵入方向と逆方向に移動させることで侵入状態を解消します。接触が1つだけなら侵入距離だけ移動させれば良いのですが、複数接触した場合はすべての接触を解消する位置を求める必要があります。正確な解(位置)を求めるには計算が複雑になってしまうため、簡略化した計算で近似値を求める方法を採用します。少しずつ移動させ、その処理を繰り返し行うことで近似解を求めます。

侵入解消処理におけるinternal edge collision問題

btBvhTriangleMeshShapeなど三角形ポリゴンメッシュでの接触法線の問題はbtAdjustInternalEdgeContacts関数で解決できますが、前述の近似値を求める侵入解消処理では別の問題が発生します。図のようにポリゴン境界に侵入すると、侵入解消処理での移動距離がポリゴン単体に侵入したときの2倍移動することになります。これにより仮想的な段差がポリゴン境界に現れます。完璧に侵入解消すればこの問題は発生しませんが、近似値を求める方法では少しコリジョンに侵入した状態になってしまうため、この問題が発生します。万能な解決策がないため、反復回数を増やす、ポリゴン境界か判定して移動量を補正、接触距離が最大ものだけ処理する、など状況によって使い分けや組み合わせが必要になります。


Continuous(連続)コリジョン判定によるキャラ移動

convexSweepTestにより、Continuous(連続)コリジョン判定が可能になります。キャラを移動させたときに衝突する位置が検出できるため、正確なコリジョンと高速移動してもトンネリングが発生しません。コリジョン判定が正確すぎるため、壁や小さな段差でも引っかかって移動できないかったり、操作性が悪くなります。今回キャラ移動を実装する際に考えた解決方法をいくつか紹介していきます。

水平垂直方向の分離

ジャンプや落下をさせる場合、重力加速度を作用させるため移動方向が斜め下となり地面を移動できなくなります。またconvexSweepTestは判定を安定化のためにわざとコリジョンへの侵入を許すようになっているため(Part2参照)、侵入解消処理を行う必要があります。しかし、その処理を坂道で行うと坂道をゆっくり下っていく問題が発生します。この2つの問題を解決するために水平垂直方向に分離し、それぞれの方向でconvexSweepTestと侵入解消処理を行います。

持ち上げ移動

convexSweepTestは正確なコリジョン判定を行うため、小さな段差や緩やかな坂であっても衝突して先に進めないと判定されてしまいます。これらの問題を解決するために一旦持ち上げて移動させ、持ち上げた高さ+重力落下分下に移動させます。この処理により、小さい段差を無視した移動と坂道を登ることが可能になり、操作性がよくなります。

壁際移動と滑り落ち

壁に接触している場合、壁内部方向への移動量が少しでもあれば衝突と判定されてしまい、壁に張り付いた様な挙動となり操作性が悪化します。壁に衝突した場合、移動方向を壁と垂直(法線)成分と平行成分に分離し、平行成分のみの移動に置き換えることで、壁に沿って滑らかに移動させることができます。これにより操作性が大幅に向上します。これと同じ原理で、落下方向を斜面と平行成分に分離することで斜面を滑り落ちる処理も実装できます。


まとめ

Bulletのコリジョン判定を使えば簡単にキャラ移動ができると考えていましたが甘かったです。操作性を無視するなら簡単だったのですが、操作性はゲームとって重要な要素の1つなので様々な方法を試行錯誤して何とか完成させました。通常の3Dゲームであればここまでする必要はなかったと思います。障害物となる剛体が自由に動くというゲーム仕様のため、かなり複雑なものになってしまいました。
簡単なものから複雑なものまでキャラ移動の方法を紹介しましたが、ゲーム制作の参考になれば幸いです。紹介した方法は個人的に考えたものなので、もっと良い方法があるかもしれません。


サンプルプログラム

キャラ移動処理のサンプルプログラムです。キーボードまたはゲームパッドでキャラの移動とジャンプ、カメラ操作ができるようになっています。
Bulletライブラリのソースコード同梱なので、Bulletライブラリの環境構築は不要です。

“Bulletでゲームを作ってみるPart3 キャラの移動” をダウンロード DX11BulletGame2.zip – 26 回のダウンロード – 1 MB


Bulletでゲームを作ってみる

Bulletでゲームを作ってみるPart1 舞台の準備
Bulletでゲームを作ってみるPart2 キャラ移動の準備
Bulletでゲームを作ってみるPart3 キャラの移動

「Bulletでゲームを作ってみるPart3 キャラの移動」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.