キャラ操作を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と同じように段差を越えたり、坂を登れるようにする

--ある程度動くキャラクターが出来たのでスクリプトを大幅に変更しました(2017/05/08)--

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

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

そこでRigidbody+コライダのセットでもCharacterControllerと同じような設定をして坂等も昇れるようにしていきます。

やり方としてはキャラクターの前に坂や壁があるかどうかを調べるレイを飛ばし(足元あたりから)キャラクターとレイが衝突した壁の角度が指定した角度以下だったときに
坂を登れるようにし上向きの移動値を計算します。

昇れる段差であれば角度を関係なく上向きの移動値を計算します。

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

地面を検知するレイを飛ばす位置

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

実際のレイを飛ばす位置

↑がRayPointの位置です。

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

それでは先ほど言及した事を考慮してスクリプトを作成します。

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

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

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

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

レイの使い方は

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

を参照してください。

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

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

この角度がインスペクタで設定したslopeLimit以下の時、または昇れる段差(stepOffset)の高さから前方にレイを飛ばし接触していなければ

上方向と押したキーの方向のvelocityを計算しています。

その部分が

ですね。

Quaternion.FromToRotation(Vector3.up, stepHit.normal)で上方向とレイの接触面の間の角度を計算しそれにtransform.forwardをかける事で坂の角度方向へのベクトルを作成しています(坂の下から坂の上への方向ベクトル)。

接触面から計算した角度方向はy座標だけを使ってx、y座標の移動はキャラクターの向いている向きで計算します。

目的の方向ベクトルを計算するまで

図にすると↑のような感じです。

角度を求めたらキャラクターの進行方向をかけることで坂の上の方向のベクトルが計算出来ます。

ここら辺はちょっと難しいですね・・・。

UnityのRaycastHit.normalとQuaternion.FromToRotationを使いこなす
UnityのQuaternion.FromToRotationで2つの方向から角度を求める事が出来ますが、その時の方向にPhysics.Raycastで得られたRaycastHit.normalの値を使う事が多々あります。このRaycastHit.normalとはなんなのか?を調べました。

ここら辺の記事を参照して頂くと衝突面の角度辺りの事に触れています。

後はキャラクターの移動ベクトルの計算でinput.magnitude * 2という処理をしていましたが、なぜこれをしていたのか不明です・・・・(^_^;)

キャラクターの向いている方向×キャラクターの移動値

の計算に変更しました。

Rigidbodyキャラクターの設定

↑がインスペクタで設定した値です。

設定値によってはうまく動作しない可能性もあるのである程度動かして確認する必要があります。

スクリプト名は改造版なのでSaishouRigidMove3となってますが名前は気にしないでください。

接地判定等がうまくいっておらずうまく動いていない部分があったので少し修正を入れました。

ジャンプをした後にすぐにレイを飛ばして接地判定しては困るのでジャンプしてから0.1秒はレイの判定をしないようにしました。

また一部条件を変更していたり、Rigidbodyの重力のオン・オフの処理を追加しています。

Rigidobodyの重力と自前の重力計算の両方を使っているので多少動作におかしな所が出てしまいますが・・・(^_^;)

以前よりもよりCharacterControllerの移動に近づけたのではないかと思います。

RigidbodyのUse Gravityにチェックを入れると重力が働き、他のゲームオブジェクトにも力を与える事が出来ますが、自身にも重力が働いており斜めの坂等では滑り落ちていきます。

これに対処するには地面にPhysicマテリアルを設定し滑らないようにするか、もしくはRigidobodyの重力を使わず自前で重力を計算すると滑らないようになります。

ただしRigidbodyの重力を使わないという事はシーソー等の床を作成した時に力を加えないのでシーソーしないようになります。

これではCharacterControllerとの違いがほとんどないのでRigidbodyにした意味は薄れます。

しかしシーソーの下からジャンプした時にすり抜けてシーソーの上に飛び乗ってしまうということは発生しないのでこれはRigidbodyの恩恵と言えるかもしれません。

ここら辺りの問題点は

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

でも触れているので参照してください。

設定値の角度より高い坂も昇れたり、キャラクターに重力が働いている為、低い坂が昇れなくなったりします。

そこら辺はRigidobodyの重力が働いていたりするので厳密な判定は難しいところですね・・・。

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

重力の操作を変更する

さきほどのスクリプトではジャンプでの移動値とRigidbodyの重力値のオン・オフを使っていますが、Rigidbodyの重力値だけを使って処理をする事も出来ます。

rigid.useGravityにtrue、falseを入れている処理を使わないので全部削除します。

ジャンプ処理のスクリプトを変更します。

Rigidbodyのvelocity(速度)のY座標にjumpPowerを入れるようにし直接Rigidbodyのvelocityを操作します。

こうすることで独自のジャンプ処理をさせずにRigidbodyの重力だけでキャラクターのジャンプをさせる事が出来ます。

キャラクターの移動に関してもRigidbodyのMovePositionではなくvelocityの値を変える事で移動させる事も出来ますが、Rigidbodyのvelocityを直接操作するのは

非現実的な物理シミュレーションに繋がるらしいのでやめておきました。

処理を変えた事で壁に突入している時はジャンプ中などはRigidbodyの物理シミュレーションのせいかひっかかる感じになりますね。

あとはジャンプ力の設定を多くしないとRigidbodyのvelocity操作だとあまりジャンプ出来ません。

独自ジャンプにするかはたまたRigidbodyのvelocityを操作してジャンプするかはあなた次第です!(^^)/

コメント

  1. ZETTA より:

    リジットボディーとコライダーで操作キャラクターを作っているのですが,壁などのコライダーを設定している障害物に当たると操作キャラクターが痙攣してしまうのですが,どのようにすれば痙攣を抑えることができますか?

    • 記事通りに作成しても痙攣してしまいます。(^_^;)

      接地判定部分がうまくいっていなかったり、処理抜けがありました。

      また一部おかしいところもあったのでスクリプトを修正しました。

      なかなかCharacterControllerと同じように動かすのは難しいですね・・・(^_^;)