今回はUnityのアクションゲーム等でキャラクターが高い所から落ちた時に落ちている時のアニメーション、
落ちてから地面に着地する時に着地するアニメーションを再生するようにしたいと思います。
高い所から落ちると言っても、10cmぐらいの高さから落ちた時も落ちるアニメーションを再生するとおかしい事になるので、ある程度の高さから落ちた時だけ再生するようにする為、レイを使って地面との距離を調べます。
着地に関してもレイを使って地面との距離を計り、一定の距離に来たら着地アニメーションを再生させるようにします。
サンプルの舞台を作成する
まずはサンプルの舞台を作成していきます。
特別こうしなければいけない!
というのはなく、元になる地面と高い場所を作成し、キャラクターが落下出来る場所を作るだけです。
↑のような感じで階段と坂で高い場所にキャラクターが移動出来るようにします。
↓のように空のゲームオブジェクトを作り名前をFieldとし、その中にスタンダードアセットのPrototypingの素材を使って舞台を作りました。
Prototypingについては
を参照してください。
Fieldを含め子要素の地面となるゲームオブジェクトにはFieldレイヤーを作り設定します。
これでサンプル舞台が完成しました。
キャラクターの作成
キャラクターのCharacterControllerを使った基本的な移動やAnimatorの作り方に関しては
を参照してください。
(注)
スクリプトで人型のボーンを取得し、そこからレイを飛ばして地面を確認するので、キャラクターモデルのインスペクタでRigのAnimation TypeをHumanoidにしておく必要があります。
人型以外でも例を飛ばす位置をHumanBodyBonesからレイを飛ばすのではなく空のゲームオブジェクトを作ってそこからレイを飛ばしたり、キャラクターの基点からオフセット値を加えた位置からレイを飛ばしても同じように出来ます。
使用するアニメーションの用意
今回使用する落ちている時のアニメーションと着地のアニメーションを用意します。
Idle、Walkの時のアニメーションはスタンダードアセットにあるアニメーションを使用してください。
落ちている時のアニメーション
まずは落ちている時のアニメーションですが、これはAssets→StandardAssets→Characters→ThirdPersonCharacter→Animation→HumanoidIdleJumpUpをCtrl+Dキーでコピーし使用します。
コピーしたら名前をFallにします。
落ちるアニメーション以外のClipを削除し、PoseのBakeにチェックを入れていきます。
アニメーションは
↑のような感じです。
着地のアニメーション
着地のアニメーションはアセットストアでraw mocap animationで検索して出てくる無料のアニメーションを使用します。
インポートしたらAssets→RawMocapData→Animations→Interacting→Idle_JumpDownHigh_IdleをCtrl+Dキーでコピーします。
コピーしたら名前をLandingに変更し、アニメーションを切り取ります。
アニメーションの切り取りに関しては
を参照してください。
アニメーションは
↑の部分を使用します。
AnimatorControllerの作成
キャラクターに設定するAnimatorControllerを作成していきます。
アニメーションパラメータにBool型のFallとLandingを作成し、Fall状態にFallアニメーション、Landing状態にLandingアニメーションを設定し、以下のような遷移を作成します。
Idle→FallはFallがtrueになった時
Fall→IdleはFallがfalseになった時
Walk→FallはFallがtrueになった時
Fall→LandingはLandingがtrueになった時
Landing→IdleはLandingがfalseになった時
でそれぞれの遷移のアニメーションブレンドは、
↑のようにすぐに切り替わるようにします。
またLandingからIdleへの遷移条件のSettingsのInterrupion SourceはNext Stateにし、着地後すぐに移動したら歩くアニメーションへと遷移するようにします。
Landing状態を選択し、インスペクタでFoot IKのチェックを入れます。
またAdd Behaviourで新しいビヘイビアを作成し、取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using UnityEngine; using System.Collections; public class EndLanding : StateMachineBehaviour { // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state //override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // isGroudedで判定する事も出来るが、動かしていない時はある程度再生してからLandingをfalseにする if (animator.GetCurrentAnimatorStateInfo (0).normalizedTime >= 0.8f) { animator.SetBool ("Landing", false); } } // OnStateExit is called when a transition ends and the state machine finishes evaluating this state //override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateMove is called right after Animator.OnAnimatorMove(). Code that processes and affects root motion should be implemented here //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateIK is called right after Animator.OnAnimatorIK(). Code that sets up animation IK (inverse kinematics) should be implemented here. //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} } |
OnStateUpdateでアニメーションの再生が80%終了したらアニメーションパラメータのLandingをfalseにします。
通常のスクリプトでcharacterController.isGroundedで地面と接地した時にLandingをfalseにする事も出来ますが、それだと着地アニメーションの再生が終わる前に遷移してしまう為、
ビヘイビアを使ってLandingをfalseするようにしました。
キャラクター操作スクリプトの作成
キャラクター操作スクリプトを作成していきます。
設定部分とStartメソッド
まずは設定部分とStartメソッドから記述していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using UnityEngine; using System.Collections; public class LandingChara : MonoBehaviour { private CharacterController characterController; private Animator animator; private Vector3 velocity = Vector3.zero; // 歩く速さ public float walkSpeed; // 地面から離れたとする距離 public float distanceToTheGround = 2.5f; // 着地アニメーションへと変わる距離 public float distanceToLanding = 2.5f; // 地面との距離を計る為のレイを飛ばす位置 private Transform spine; // Use this for initialization void Start () { characterController = GetComponent<CharacterController> (); animator = GetComponent <Animator> (); spine = animator.GetBoneTransform (HumanBodyBones.Spine); } } |
distanceToTheGroundはキャラクターが地面と接地していない時に下の地面との距離を入れます。
地面から落下する時にこの距離以上だったら落下アニメーションにします。
distanceToLandingは落下アニメーション中に下にレイを飛ばし、地面との距離を計り、この距離以内になったら着地アニメーションにします。
spineはレイを飛ばす元の位置です(ボーンを設定する為、そのボーンの名前にしました)。
StartメソッドでSpineのボーンを取得し設定しています。
Updateメソッド
Updateメソッドでは地面に接地している時、落下時、落ちているアニメーションの時で処理を分岐させています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // Update is called once per frame void Update () { // 地面に接地している時 if (characterController.isGrounded) { // 着地したらアニメーションパラメータのFallをfalseにする animator.SetBool ("Fall", false); velocity = Vector3.zero; var input = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); if (input.magnitude > 0f) { // 着地後移動キーを押したら着地アニメーション終了 animator.SetBool ("Landing", false); transform.LookAt (transform.position + input); velocity += transform.forward * walkSpeed; animator.SetFloat ("Speed", 1f); } else { animator.SetFloat ("Speed", 0f); } // アニメーションパラメータFallがfalseの時で地面との距離が遠かったらFallをtrueにする } else if (!animator.GetBool ("Fall")) { Debug.DrawLine (spine.position, spine.position + Vector3.down * distanceToTheGround, Color.red); if (!Physics.SphereCast (new Ray (spine.position, Vector3.down), characterController.radius, distanceToTheGround, LayerMask.GetMask ("Field"))) { animator.SetBool ("Fall", true); } // 落下アニメーションの時はレイを飛ばし着地アニメーションにする } else if (animator.GetBool ("Fall")) { Debug.DrawLine (spine.position, spine.position + Vector3.down * distanceToLanding, Color.green); if (Physics.Linecast (spine.position, spine.position + Vector3.down * distanceToLanding, LayerMask.GetMask ("Field"))) { animator.SetBool ("Landing", true); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move (velocity * Time.deltaTime); } |
キャラクターが接地した時はアニメーションパラメータのFallをfalseにしています。
また、移動した時はアニメーションパラメータのLandingをfalseにし着地アニメーションを終了させています。
地面に接地していない時でアニメーションパラメータのFallがfalseの時はキャラクターのspineから下にレイを飛ばし、地面に接地しなければアニメーションパラメータのFallをtrueにし落ちる時のアニメーションにします。
ここで落ちる高さが低い時は落ちる時のアニメーションにはしません。
落下アニメーションの時はspineから下にレイを飛ばし、地面に接触したら着地アニメーションへと遷移させています。
これで全ての機能が完成しました。
高い所から落下し確認する
機能が完成したので、実際にキャラクターを動かし低い所と高い所から落下させて確認してみましょう。
キャラクターのインスペクタは↓のように設定しました。
↑のようになりました。
細かいアニメーションの切り替えがあると少し本格的になった感じがしますね。(^^)/