UnityのCharacterControllerの接地が判断出来ない時にスクリプトを使う

キャラクターの移動にはCharacterControllerのMove関数を使っていましたが、その移動の条件として、キャラクターが地面に接地している事という条件を設定していました。

ジャンプをするにも接地している必要があります。(空中でジャンプOKなら別ですが)

またCharacterControllerではなくRigidbody+コライダでキャラクターを動かしている場合の接地の判断としても今回の記事は有効です。

ジャンプ機能に関しては

Unityのゲームでキャラクターがジャンプ出来るようにする機能を作成します。

を参考にしてください。

キャラクターの接地はCharacterControllerのisGroundedプロパティを調べればCharacterControllerのコライダが地面に接地しているかどうかが簡単に調べられます。

ジャンプの機能を実装するまでは全然気づかなかったんですが、平たんな道や坂を登る時は問題ないんですが、坂を下りながらジャンプボタンを押すとジャンプが出来ないんです。

接地7

上の動画のように坂を下りながらジャンプボタンを押してもジャンプしてくれません。

その原因は坂を下る時にコライダが接地面から外れisGroundedプロパティがfalseになってしまっているせいです。

どんな具合に接地面から外れるか確認してみます。

スポンサーリンク

CharacterControllerのisGroundedプロパティで接地が確認出来ない理由

接地1

坂を登る時はコライダが矢印の方向に進むのでisGroundedプロパティはtrueになります。
登る時は問題ないですね。

接地2

次に坂を下る時です。
コライダが矢印の方向に進むので移動している間はisGroundedプロパティはfalseになってしまいます。

ジャンプ機能を使用しなければこの問題を考えなくてもいいんですが、ジャンプ機能を使ったアクションゲームを作る場合、これはかなり大きな問題です。

というわけで、CharacterControllerのisGrounded以外の接地を確認する方法が必要になります。

レイを使わないパターンを追記しました(2018年07月27日)。レイを使わないパターンの方が簡単です。

Rayを飛ばしキャラクターの接地を確認する

まずはキャラクターからRayを飛ばし、地面に接地しているかどうかを調べる方法をやってみます。

具体的にはPhysics.Linecast関数を使いキャラクターの体の一部をスタート地点にして、下方向に指定した距離のレイを飛ばします。

isGroundedだけで接地条件としていた個所にレイを飛ばして地面と当たっていた場合も含める事にします。

少しづつ見ていきます。

rayPositionはレイを飛ばす位置、rayRangeはレイの距離でインスペクタで設定出来るようにします。

isGroundはレイが地面に到達したかどうかのフラグに使用します。

次にUpdateメソッドの最初でCharacterControllerのisGroundがfalseの時にレイを飛ばして接地を確認します。

CharacterControllerのisGroudedプロパティがfalseの時だけレイを飛ばすようにします。

isGroundedプロパティがtrueの時は接地しているのがわかっているのでレイを飛ばす必要がない為です。

Physics.Linecastは

なのでレイを飛ばす開始地点と終了地点を指定します。

開始地点はインスペクタでキャラクターの体の一部を指定します、今回はStandardAssetsのキャラクターであるEthanのEthanHipsを指定します。

終了地点はレイを飛ばす開始地点+飛ばす先で計算した位置を指定します。

飛ばす先は

-transform.upでキャラクターの上向きの逆、なのでキャラクターは地面と垂直にしているので、下向きであるVector3(0, -1, 0)になります。

それにrayRangeをかける事でVector3(0, -charaRayRange, 0)になります。

rayPosition.positionにその値を足す事で開始地点に飛ばす先の位置を足したので終了地点となります。

rayPosition.position + (-transform.up * charaRayRange)

rayPosition.position – transform.up * charaRayRange

となります。

レイが地面と接触していればisGroundにtrueを入れます。
接触していなければfalseを入れます。

今回はレイを飛ばして接触した相手を指定していないので、地面じゃない物にレイが接触した場合もisGroundにtrueが入ってしまいます。
通常は地面にするゲームオブジェクトにレイヤーを指定し、そのレイヤーと接触していたかどうかで判断します。

地面に設定するレイヤー名をFieldとしていた場合

とレイヤーを指定します。

isGroudedだけで判断していた接地にisGroundがtrueである時も加えます。

接地していた時はvelocityにVector3.zeroを入れていましたが、レイを飛ばして当たった場合は方向キーの値だけを初期化し、重力の値はそのままにします。

レイが地面に当たっていた時のisGroundはあくまでジャンプする時に使用するものなので、重力値の初期化はisGroudedプロパティがtrueであった時だけにします。

重力の処理以外は通常のキャラクターの移動と同じです。

下り坂を移動中にジャンプ出来るかどうかを確認

それでは機能が出来上がったので設定をしてUnityの実行ボタンを押して確認してみます。

接地3

MoveTest(キャラクターを移動させるスクリプト)のインスペクタでcharaRayにEthanHips、charaRayRangeに0.85を設定します。
キャラクターによって設定する位置や距離は異なります。

接地6

上のような感じで、レイが地面より少し下に到達するような感じで指定します。

接地4

上の動画のように坂を下っている途中でもジャンプが出来るようになりました。
もしジャンプが出来ないようであればレイの距離を少し伸ばしてみてください。

接地5

上のようにCharacterControllerのコライダが接地している時はレイを飛ばさず、接地していない時にレイを飛ばしています。
前述したようにコライダは坂を下る時は接地判定されません(動いている時)

レイを使わないで坂を下っている時にジャンプする方法

2018年07月27日に追記した内容になります。

レイを使わずとも坂道を下っている時にジャンプする事が出来たのでそちらの方法もやってみましょう。

接地が確認出来ないのは横に移動した時に空中にいる状態になってCharacterControllerのisGroundedがfalseになってしまう事でジャンプが出来ませんでした。

そこでisGroundedがtrueになった時に下向きのオフセットの移動値を加えることによって空中状態をなくします。

addForceDownPowerが強制的に加える下向きの力になります。

Updateメソッドに入った時にVector3.zeroに初期化し、CharacterControllerのisGroundedプロパティがfalseになった時はVector3.downで下向きの値を入れます。

ジャンプボタンを押した時に強制の下向きの力が働くと困るので、Vector3.zeroを入れています。

CharacterControllerのMoveメソッドの引数にaddForceDownPowerを加えます。

これで坂を下っている時もジャンプ出来るようになりました。

レイを使うよりスクリプトの流れもわかりやすくなって処理もうまくいっているようなのでこちらの方がいいかも?

これでUnityのCharacterControllerの接地をスクリプトで判断する事が出来るようになりました。

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます