キャラ操作をCharacterControllerからRigidbody+コライダに変更する

わたくしは今までキャラクターの当たり判定や移動の機能に関してCharacterControllerを使用してきました。

CharacterControllerコンポーネントを使用するとコライダ+移動機能が付いているので簡単にキャラクターの移動を実現出来たからです。
ですが、

UnityのPhysic Materialを使って衝突や跳ね返りを設定する
UnityのPhysic Materialを使って衝突や跳ね返りを設定します。衝突したらそのまま勢いを無くしたり、スーパーボールのように反発して跳ね返るというような処理を作りたい時にPhysic Materialは便利です。

や、

Unityでキャラクターが乗ったらシーソーする床を作成する
UnityでCharacterControllerで操作するキャラクターが乗ったらシーソーする床を作成します。

等の機能を作成しているうちに相手方のゲームオブジェクトの物理的な影響を受けないと実装したい機能が不十分になる事があります。

細かいアクションが売りのゲームを作るとなると物理的な影響を受けないというのはかなり不便です。

衝突の影響をそれほど考慮しなくていい時はCharacterControllerの方が便利で楽です。

作成するゲームによって変更するといいかもしれません。

スポンサーリンク

CharacterControllerを削除しRigidbodyとCapsuleColliderを取りつける

CharacterControllerでキャラクターを動かしていたものをRigidbody+CapsuleColliderに変更するのは大変なんじゃないか?

と思ったんですが、やってみたらそれほどでもありませんでしたので、紹介したいと思います。

と思っていたんですが、制御がめちゃくちゃ大変でした・・・(T_T)

移動だけならともかくジャンプ機能を搭載したら全然うまくいかなくなり四苦八苦しておりました・・・・(^_^;)

今でも何らかの不具合発生しそうで怖いですが、とりあえず動くようになったので・・・・(-.-)

キャラクターのCharacterControllerを削除し、

Add Component→Physic→RigidbodyとCapsuleColliderを取りつけます。

rigidbodyとcapsulecolliderで動かすキャラクターのインスペクタ

↑のようにRigidbodyのDrag、Angular Dragに1を入れ受ける力に対しての空気抵抗を指定しておきます。

空気抵抗がある為受ける力が段々弱くなるようにし、どこまでも力を受ける事がないようにしておきます。

Use Gravityにチェックを入れキャラクターに重力を働かせます。

ConstraintsでFreeze Rotationにすべてチェックを入れます。

外部からの物理的な影響で回転しないようにします。

ここにチェックを入れないと物理的な影響でキャラクターが回転してしまい制御出来なくなります。

Freeze Positionはチェックを外し、物理的な影響でキャラクターが移動するようにします。

これは外部からの影響もありますが、キーを押して自分でキャラクターを動かす時にもここでチェックがされてると動かなくなる為チェックを外す必要があります。

最小限のキャラクター操作スクリプト

次に最小限のキャラクターの移動スクリプトを修正してみます。
キャラクターの移動とジャンプ機能だけを搭載したスクリプトです。

スクリプトの変更点を考える

スクリプトのCharacterControllerを使った移動で、CharacterControllerのisGroundedプロパティが使えなくなるのでCapsuleColliderが地面と接地しているかを別の個所で調べるようにします。

また、移動の処理をCharacterControllerのMoveからRigidbodyのMovePositionに変更します。

MovePositionで指定する引数は移動先のVector3を指定する必要があるので、現在地+入力値で計算します。

Rigidbodyでの移動処理はUpdate関数ではなくFixedUpdate関数で行う必要があるので、移動処理だけFixedUpdate関数内に記述するようにします。

Rigidbodyを使った物理的な移動等はFixedUpdate関数で行った方がいいようです。

スクリプトで計算していたキャラクターにかける重力はRigidbodyで働かせるようにしますが、ジャンプ中はその機能を無効化し、スクリプトで重力を計算します。

これはジャンプ機能はvelocity.yに値を足す事で実現していて、MovePositionで指定する値が移動先のVector3である為にRigidbodyの重力とは別にスクリプトでこのvelocity.yの値を減らしていかなければいけません。

つまりRigidbodyで重力は働きますが、velocity.yの値を減らしているわけではないので独自に減らす必要がある。

ということですね。

RigidbodyとCapsuleColliderを使った移動に切り替えたスクリプト

それではさきほどの変更点を考慮してスクリプトを修正します。

↑が変更を加えた結果です。

ほとんど変わっていないですね。
うそつけーーー(-_-)/~~~ピシー!ピシー!

コライダが地面と接触しているかどうかはOnCollisionEnterで他のコライダとの接触を検知し、さらにキャラクターの下にそのコライダがあるかどうかを
レイを飛ばして確認します。

ゲーム内の地面となるゲームオブジェクトにはFieldまたはBlockレイヤーが設定されている必要があります。

他のレイヤーも判断したい時はレイヤー名を追加してください。

レイヤーの判定は要らないと言えば要らないかもしれません(キャラクター自身を外して)

細かい部分はコメントを見てください。(´Д`ι)アセアセ

キャラクターが動くかどうか確認する

CharacterControllerで動かしていた時と違いが出てないか確認してみましょう。

RigidbodyとCapsuleColliderでキャラクターを動かすサンプル

基本的な移動も出来、他のゲームオブジェクトにも自動的に物理的な力が加わっています。

他のゲームオブジェクトからの影響も受けるのでシーソーの床に乗ると少しずつ坂を落ちていきます。

青い坂の方にはPhysic Materialを設定し摩擦を大きくしたのでキャラクターが落ちていく事がありません。

また動画内にはありませんが、最初に紹介した記事でキャラクターがジャンプや移動した時にすり抜けてしまう現象が回避されるようになりました。

やったね(^^)v

これで問題が出なければ自分のゲームでキャラクターを使う時はRigidbody+CapsuleColliderで作ろうかな・・・・(-.-)

ひとつ問題があるとすれば、Rigidbody+CapsuleColliderでキャラクターを動かす時に段差を越えられないという事でしょうか。

ちょっとした段差でもコライダが引っかかって動けません。

CharacterControllerとRigidbody+CapsuleColliderのキャラクターの動きを比較する

最後にCharacterControllerとRigidbodyで動かすキャラクターを横に並べて確認してみましょう。

左側がCharacterControllerで右側がRigidbodyで動かしているキャラクターです。

最後にどちらがどちらか確認するのでよく見てください!!(ウソです)

CharacterControllerとRigidbodyでの違いを確認するサンプル

制御が違うので多少違いが出てますね。

Rigidbody君の方は接触している床の摩擦を受けるので坂を登るスピード等は遅くなりますね。

CharacterControllerと同じように段差を越えたり、坂を登れるようにする

さきほど段差を越えられないと言いましたが、デコボコの坂も登れません。

さきほどのサンプルのようになだらかな坂限定のゲームにするとかジャンプでデコボコな坂も登るとかそういう感じで作る必要があるかもしれません。

ちょっと動作が怪しげですが、CharacterController風の段差越えとデコボコ坂を登れる機能を作ってみました。

あらかじめ言っておきますが、たぶんもっとかしこいやり方があると思います。

それでもよければ参考にしてください。

やり方としてはキャラクターの前に坂や壁があるかどうかを調べるレイを飛ばし(すねあたりから)キャラクターとレイが衝突した壁の角度が指定した角度以下だったときに
Y軸の上方向に力を加えます。

今回の場合はvelocity.yの値に力を足します。

ただレイが衝突している時に力を足すだけでは動かしていない時もピョンピョン飛び跳ねてしまうので、条件としてキャラクターを動かしている事というのを加えます。

また段差を越える為に別の場所(ひざ上あたり)からレイを飛ばし、『すねからのレイ』が壁と接触していてかつ『ひざ上あたりからのレイ』が接触していない時にも
Y軸の上方向に力を加えます。

これで坂を登る時以外の段差がある場所に登りたい時の判定も出来ます。

それではキャラクターから前の坂や壁を検出出来るようにレイを飛ばす場所RayPointと
越えられる段差を調べる時に使うレイを飛ばす場所StepOffsetPositionをキャラクターの子要素に空オブジェクトで作成し位置を調整します。

レイを飛ばす位置を作成

↑のようにキャラクターの子要素に作成します。

RayPointの位置と向き

↑がRayPointの位置です。

Local表示での青矢印がキャラクターの前向きになるようにRayPointを回転させます。

RayPointのTransform

↑がRayPointのTransfromになります。

StepOffsetPositionの位置と角度

↑がStepOffsetPositionの位置です。
こちらも青矢印がキャラクターの前向きとなるように回転させます。

StepOffsetPositionのTransform

↑がStepOffsetPositionのTransformになります。

それでは先ほど言及した事をスクリプトに追加していきましょう。
まずは新しい変数を追加します。

キャラクター操作スクリプトの重力値を計算した後に次のスクリプトを取りつけます。

レイが地面や壁と接触しているかどうかはPhysics.Linecastで調べています。

Physics.Linecastは始点から終点に向けてレイを飛ばしてぶつかった相手をstepHit変数に入れます。

接触相手は『Field』、『Block』というレイヤーに設定されているゲームオブジェクトのみを調べています。

その為地面や壁などはこの2つのどちらかのレイヤーを設定している必要があります。

レイの使い方は

Unityでよく使う便利な標準関数について
管理人のかめくめちゃんがUnityのゲームを作成する時によく使う関数を紹介します

を参照してください。

キャラクターが移動しているかどうかは

input.magnitudeの値が0.5f以上の時という条件にしています。

またキャラクターの上方向(transform.up)とレイが接触した方向(stepHit.normal)の角度をVector3.Angleで求めています。

Vector3.Angleで2つの方向の角度を求める事が出来ます。

この角度がインスペクタで設定したslopeLimit以下の時は上方向に力を加えています。

条件に満たなかった時はY軸のvelocityを0に設定します。
これがないと坂を登りきった後に一瞬ピョンと跳ねてしまいます。

これで坂を登ったり段差を越えたりする事が出来るようになりました。

ただレイを飛ばす位置や変数の設定値によって動作が不安定になります。
今回の設定値あたりから変更するのがいいかもしれません。

坂、段差を登る時のスクリプトの設定値

↑が今回設定したパラメータです。

スクリプト名は気にしないでください。(^_^;)

それでは確認してみましょう。

Rigidキャラで坂、段差を登るサンプル

↑のように段差、坂を登る事が出来ました。

が勢いをつけていかないとひっかかったりします。

もう少し綺麗に登れるように出来たらいいんですけどね・・・・難しいなぁ・・・(-.-)
何か他にいい方法がないだろうか・・・・。

これでRigidbodyを使ったキャラクターの移動とジャンプの処理、坂や段差を登る事が出来ました。