壁の摩擦の設定をどうするか [ゲーム開発ログ 2020-11-13]

2020-11-15

Rigidbody と Physics Material の摩擦

Unity で 3D のゲームを作ろうとしたら、普通は Unity 組み込みの物理演算を使う。 動かすキャラクターに Rigidbody と Capsule Collider を当て、Rigidbody の velocity を制御して移動させる。 僕も普通にそうしている。

さて、そうやって素朴に作っていると、 「落下中やジャンプ中に壁に引っかかる」 という現象に出くわす。


デフォルトの Physics Material だと壁に引っかかる

Unity のデフォルトの Physics Material には(たぶん 0.5 くらいの)摩擦が設定されているので、 壁に向かって移動し続けていると摩擦で引っついてしまうのだ。 期せずして 壁走り機能 が実装されてしまっていた。

多くのゲームでは、何の代償もなく壁走りができてしまっては困るだろう。 レイキャストして壁判定を行う方法もあるが、 単純な解決方法は 摩擦を 0 にした Physics Material を用意して、壁オブジェクトに当てる ことである。 これにより壁に引っかかることなくストンと落ちてくれる。

自分は壁に沿うような移動時にも摩擦がなく滑るように動いてくれるのが好みだったので、 全ての壁を摩擦 0 に設定するので問題がなかった。 …と思っていたが、 急斜面の壁 を歩いたときに問題があることがわかった。

摩擦 0 だと急斜面が登れて大ジャンプできてしまう問題

摩擦 0 だと、急斜面に向かって移動した際に、かなりの急斜面でも登れてしまった。 しかも 登った勢いで思いのほか大ジャンプ ができてしまった:

動画では音楽もつけて なんかそういうゲームみたいな雰囲気 になっているが、 実際問題、何の代償もなく急斜面を駆け上がって大ジャンプされてしまっては困るだろう。

大ジャンプについては、y 方向の加速度を一定以上高くならないように補正してやれば十分だったりする。 y 方向の加速度は、マイナス側も抑えてやると「一定速度でふわっと落ちる」ような挙動にも調整できる。 ということで自分は以下のような感じで y 方向の加速度を Clamp して大ジャンプや急速落下を防ぐようにしている:

// -4.5f 〜 2.5f くらいに丸めてやると穏やかな動きになる
float velocityY = Mathf.Clamp(_rb.velocity.y, minVelocityY, maxVelocityY);
_rb.velocity = new Vector3(
    moveForward.x * moveSpeed * Time.fixedDeltaTime,
    velocityY,
    moveForward.z * moveSpeed * Time.fixedDeltaTime
);


急斜面を駆け上がれる問題は、レイキャストで接地面の角度判定みたいなものを書いてやればどうにかできる。 が、自分が今回作っているゲームは「わりと急斜面でも登れてよい」仕様なので、いったん登れてしまってよいことにしている。 垂直なコリジョンは登れないので、「ほぼ垂直に近い斜面だが登られたら困る」 オブジェクトには垂直のコリジョンを個別に指定することで対応ができるだろう。