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

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

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

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

ジャンプ機能に関しては

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

を参考にしてください。

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

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

接地7

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

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

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

スポンサーリンク

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

接地1

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

接地2

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

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

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

Rayを飛ばしキャラクターの接地を確認する処理をMoveTestに追加

今回はキャラクターからRayを飛ばし、地面に接地しているかどうかを調べます。

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

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

public var charaRay : Transform;
public var charaRayRange : float;
private var ground : boolean;

charaRayはレイを飛ばす体の位置、charaRayRangeはレイの距離でインスペクタで設定出来るようにpublicで宣言します。
groundはレイが地面に到達したかどうかのフラグに使用します。

CharacterControllerのisGroudedプロパティがfalseの時だけレイを飛ばすようにします。
isGroundedプロパティがtrueの時は接地しているのがわかっているのでレイを飛ばす必要がない為です。

Physics.Linecastは

Physics.Linecast(開始地点, 終了地点)

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

開始地点はインスペクタでキャラクターの体の一部を指定します、今回はStandardAssetsのキャラクターであるEthanのEthanHipsを指定します。
終了地点はレイを飛ばす開始地点+飛ばす先で計算した位置を指定します。

飛ばす先は

-transform.upでキャラクターの上向きの逆、なので下向きVector3(0, -1, 0)になります。
それにcharaRayRangeをかける事でVector3(0, -charaRayRange, 0)になります。

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

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

charaRay.position – transform.up * charaRayRange

となります。

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

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

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

Physics.Linecast(charaRay.position, (charaRay.position – transform.up * charaRayRange), LayerMask.GetMask(“Field”))

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

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

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

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

攻撃をする時もレイの到達フラグgroundは加味しない為、条件を加えます。

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

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

接地3

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

接地6

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

接地4

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

接地5

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

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