正しい重力には従わないキャラクターのコンポーネント設定
Unityにて3Dキャラクターのコリジョン処理を含めた移動処理を実現したい場合、
- CharacterControllerを使う
- StandardAssetsのThird Person Controller的にCollider+Rigidbodyを制御する
などの方法や、その他ゴニョゴニョするなどがありますが、
どれが最適かはそれぞれのゲームで表現したいことなどに依存するかと思います。
今回、余暇で作ってるゲームのキャラクター挙動で、
Rigidbodyもつけつつ、Physicsの重力計算は無視したくなり、
いくつか試行錯誤したので、
その結果と過程におけるキャラクターのコンポーネント構成/設定を書いてみたいと思います。
キャラクター制御に対する一つのアプローチだと思って頂ければと思います。
そもそも
上記に上げたほうほうでなにが都合が悪いのか。
CharacterController
http://docs.unity3d.com/ja/current/Manual/class-CharacterController.html
Character Controller は、主にRigidbody による物理特性を使用しない、3 人称や 1 人称のプレイヤー制御に使用されます。
Character Controllerを使うと、Rigidbodyが無くても地形・壁との接触判定をした移動をしてくれます。
逆に、Rigidbodyを想定した、その他のRigidbodyを持つオブジェクトと共有できるような普遍的なコンポーネントなどを作るのは難しくなっちゃいます。
一方、プレイヤーキャラクターが物理特性の影響をうけるようにしたい場合は、キャラクターコントローラーの代わりに、リジッドボディ を使用した方がよいでしょう。
ともあります。
Third Person Controller
Standard Assetsのサンプルシーンに含まれる
https://www.assetstore.unity3d.com/jp/#!/content/32351
人型のキャラクターEthanがフィールドを走り回るデモです。
EthanにはRigidbodyとCupsleColliderがついており、
OnAnimatorMove()でanimator.deltaPositionから平行移動、
FixedUpdate()からRigidbodyに対してジャンプ処理のためにAddForceをしています。
物理的な挙動になりますが、
細かな挙動の作成が面倒であったり、
プレイヤーの状態によって色々な挙動にしたい場合など、
ちょっと面倒に思います。
特徴的なアクションをキャラクターにさせたい場合、
例えば残像を残しずつ瞬間移動をしたりとか、
そういうときはforceではなく、座標を直接制御したくなったりします。
今回、ひとまず落ち着いた方法としては、
上の二つの中間のようなあたりのやり方になります。
設定
キャラクターにはColliderとRigidbodyをつけます。
壁等とのコリジョン処理はRigidbodyによって押し合い・壁ズリ処理してもらいます。
useGravityを無効にして、ジャンプ、落下処理は自分で行います。
一方、地面との設置判定は自前のスクリプトで、
Rayを飛ばして判定をとるようにします。
インスペクタはこんな感じ。
LayerはPlayerにしています。
これはレイキャストによる地面判定時に自分のコリジョンにレイがヒットしないように
Playerのマスクを無視するよう指定するためです。
コンポーネントは上から、
Rigidbody
SphereCollider
CharacterLocomotion(移動のための自作スクリプト)
以下はその他もろもろ
要点としてはRigidbodyの設定になります。
まず、Dragはデフォルトでゼロになっていると思いますが、
物理挙動による力が加わった場合に減衰させるための値を入れておきます。
use Gravityのチェックは外しておきます。
このケースでは落下処理はスクリプトで行います。
constrintsの設定は、回転に関して3軸固定にするためチェックを入れておきます。
物理計算時に回転が入ってしまうことを防ぎます。
Character Locomotion
これは移動処理を行う自作スクリプトです。
例えば、ジャンプボタンを軽く押した場合に短いジャンプをし、
ジャンプボタンを長押しした場合にゆっくり大きいジャンプをする、
というようなキャラクターの挙動を書いてみたとします。
途中にアニメータのフラグの設定など余計なものがありますが、あくまでひとつの例ということで
とりあえずそのままにしてます。
verticalSpeed -= Time.deltaTime * gravityScale; transform.position += new Vector3(0.0f, verticalSpeed, 0.0f) + horizontalSpeed;
で自前で落下の処理を行っています。
その処理のあとのRayCastをしているあたりが地面の判定をしている箇所になります。
さきに書いたように自分自身のLayerであるPlayerのマスクを無視するよう指定しています。
JumpInputという別のコンポーネントで、ジャンプボタンの入力をトリガーするようにしています。
短いジャンプのイベントがきたら、
verticalSpeed = 0.2f; gravityScale = 1f;
長いジャンプの入力で
verticalSpeed = 0.08f; gravityScale = 0.1f;
と重力計算パラメータを変更するようにしています。
あくまで一つの例ではありますし、
プログラムもこのままではまずいですが、
こういう実装の選択肢もあるかと思います。
この作品はユニティちゃんライセンス条項の元に提供されています