物理演算のジャンプ処理を作る【ゲーム開発日記】

Unityを使ってTPS型で物理演算のキャラ移動を作る記事の続きです!
前回の記事で壁と床を区分して移動方向を決めるところまでが出来たので、今回は空中移動・ジャンプ機能の流れについて見ていきます。

この記事が初めての方は前回の記事の方も見てみてください!↓

※この記事は2024.10.06に大幅加筆修正しました。

空中での移動

空中では床の角度を考慮しなくて良いので、Rigidbody.AddForceで移動方向への力を少しだけ加えて後は重力で自然に落ちるようにしました。
滞空中に方向転換できるのは物理的にはおかしいですが、そういう風になってるゲームって割と多いですよね。
操作できない時間ってプレイヤーにはストレスなので、ちょっとしたサービスとして少し動かせるのは大事なのかなと思って僕は取り入れてみました。

重力は初期値の-9.81だとふわっとしすぎる気がしたので、僕は-26に調整してみました。キャラのデザインや作風・ジャンルで変わりそうなので、触って動かしながら好みの値を見つけるのがよさそうです。

重力-9.81

重力-26

ジャンプの処理を作る

ジャンプをするにはRigidbody.AddForce(方向, ForceMode.Impulse)を使います。
ForceMode.Impulseは物理法則を無視して1フレームの実行で1秒間Forceを加えたのと同じにするとのことなので、ジャンプ開始の1フレームで1回だけ処理するようにします。
その後、滞空中に少し操作ができるようにする場合は通常のAddForceを使います。
※ForceModeは特に指定しなければForceMode.Forceになります。

ジャンプしたのち滞空中は床と接触したかどうかの判定を取り続け、床に接触したらジャンプ終了という流れになります。
ここで少し詰まってしまったのが、「ジャンプ開始のAddForceのすぐ後は床接触の判定が出てしまう」という点です。力が加わっても実際に接触状態から離れるのには少しタイムラグがあるようです。
そのため、「ジャンプしたのち床に接触判定が出たら終了処理」という風にしていると次のフレームで終了処理が呼ばれてしまいます。
ジャンプ直後の少しの間は終了処理に行かないように注意が必要です。

色々な動きを作るには…

ジャンプの処理を作っていて、まず動作そのものを作る前に現在の状況が分かるようにスクリプトの方で下準備をしておくことが大事だなと思いました。
作りたい動きにもよりますが、OnCollisionEnter / Stay / Exitみたいな感じで以下の判定と対象のオブジェクトが毎フレーム分かるようにしておくと色んな状況で使えるんじゃないかなと思います。

  • 床と接触開始 / 接触中 / 接触が離れた / 床と接触していない
  • 壁と接触開始 / 接触中 / 接触が離れた / 壁と接触していない

特に壁については、同時に複数接触するものすべてに処理が必要な場合があるので要注意です。
同時にいくつ接触するかはステージの設計によるので、あまり複雑にして想定外のハマりが生まれないようにした方がよさそうです。
動く壁や床・MeshColliderを使った凹凸のある構造物など扱いが複雑そうなものも考えられますが、絶対必要なところ以外は極力シンプルにした方が良い気がします。
僕の場合は「同時に触れられる壁は2つまで」と絞ってステージとスクリプトを作ることにしました。

空中での壁接触の処理

上に乗れる高さの壁にジャンプする時、壁の方向にスティックを押し込みながらジャンプする人が多いんじゃないかと思います。
ところが物理演算でジャンプ中に壁に接触しながら壁方向のベクトルで移動しようとすると、失速して落ちてしまいます。壁に触れていても壁面に沿って真上に飛び上がるのは問題ないので、接触が原因ではなく壁面に食い込む方向の移動が駄目なようです。

おそらくこれは物理演算としては正しい挙動です。
ボールとかを壁にぶつけた時に壁に沿って上がることはないので、「物理演算に沿った仕様」と考えてそのままにするのも有りだと思います。

物理に逆らってキャラが壁にぶつかっても壁に沿って浮き上がるような挙動にしたい場合、やることが2つあります。

  • 壁の物理マテリアルを摩擦なしにする
  • キャラが壁と接触している際の移動方向について、壁方向のベクトルを相殺する

それぞれ見ていきます。

壁の物理マテリアルの設定

Cubeなどの基本のオブジェクトについているコライダーはPhysic Materialが空になっていて、プロジェクトの初期値が反映されるようになっています。
これはあまり滑らない設定になっています。

これを壁オブジェクトだけ摩擦のないツルツルのものに設定します。
Projectタブの+から新しくPhysic Materialを作り、Dynamic FrictionとStatic Frictionを0にします。
またFriction Combineを0にします(これは接触した2つのオブジェクトのどちらの摩擦を優先するかという値です)。

設定が出来たら好きな名前をつけて壁オブジェクトのコライダーのMaterialに指定します。

壁が床でもある場合…

壁だけれど上に上がって乗ることもできるオブジェクトもあると思います。
この場合、物理マテリアルを適用すると床部分がツルツルになって滑ってしまいます。
このようなオブジェクトは、上にPlaneなどで作った滑らないオブジェクトを重ね置きしておくと良いかと思います。


Mesh Rendererをオフにして透明にするのと、ギリギリ床より高くなるくらいにするのがポイントです。
あまりギリギリにしすぎると貫通してツルツル床の方の判定になってしまうようなので、判定が切り替わるギリギリの高さにすると良いかと思います。

キャラの足が浮いて見えるのが気になる…という場合は、モデルのアニメーションのオフセットをずらして対応するのが良いかなと思います。

壁接触時の移動方向の処理

ジャンプ中に壁に接した場合の壁方向のベクトルは相殺して壁の面に沿ったベクトルにします。
これで壁面を滑るような動きにすることができます。

やり方は、まず移動方向が触れている壁の法線と90度以上の角度(壁面に食い込む角度)であるかどうかを判定します。
90度以上だったら前の記事でも使ったVector3.ProjectOnPlaneを使用し、平面に沿うように変換します。

※なおVector3.ProjectOnPlaneは法線との間の角が90度以上でも同じ結果になるので、そのままで大丈夫です!

複数の壁に触れている場合

複数の壁に同時に触れている場合、両方に対してVector3.ProjectOnPlaneの処理が必要です。
順番はどちらでも結果は同じなので、判定を両方取って処理すればOKです。

ここまでの仕上がり

歩きとジャンプを使ってマップの中を動けるようになりました!
次の記事でカメラの挙動を作ったら基本の動作は完成です。
そこまで出来れば3Dマップの中を自由に散歩できるのでそれだけでも楽しそうです。

あと少しなので頑張ります…!

次回の記事はこちら↓

シェアする

「Unity」の記事

もっと読む>>

最新記事

もっと読む>>