今回はCharacterControllerコンポーネントを使った人型キャラクターで、ジャンプボタンを押した時にジャンプをさせ、ジャンプアップ→空中→着地までのアニメーションの変化をさせていきたいと思います。
この機能は『シンプルなアクションゲームを作ってみよう』のカテゴリのジャンプの機能で作りましたが、そちらはRigidbody+コライダで作ったキャラクターのRigidbodyのvelocityを操作する事で行いました。
今回はCharacterControllerコンポーネントを使ったキャラクターで同様の機能を作成していきます。
今回の機能を作成すると以下のようにキャラクターがジャンプし、接地するまでその位置に応じてアニメーションが変化します。
キャラクターの設定
キャラクターモデルをヒエラルキーに配置し、インスペクタのAdd ComponentからCharacterControllerコンポーネントの取り付けを行います。
キャラクターモデルに合わせてコライダのサイズを調整してください。
AnimatorControllerの作成
次にAssetsフォルダで右クリックからCreate→Animator Controllerを選択し、名前をJumpCharacterとします。
JumpCharacterアニメーターコントローラーではアニメーションパラメータにFloat型のSpeed、Trigger型のJump、Float型のJumpPower、Bool型のIsGroudedを作成します。
次に状態としてIdle、Walkを作成し各状態を選択した状態で右クリックからMake Transitionを選択し、Idle→Walk、Walk→Idleの遷移を作成します。
Idle→WalkのConditionsの条件はSpeedがGreaterで0.1にし、Has Exit Timeのチェックを外し、SettingsのInterruption SourceをNext Stateにします。
Walk→Idleの条件はSpeedがLessで0.1にし、Has Exit Timeのチェックを外し、SettingsのInterruption SourceをNext Stateにします。
次にアニメーターウインドウ内で右クリックからCreate State→From New Blend Treeを選択し、インスペクタでBlend Treeの部分をJumpと変更します。
Idle→Jump、Walk→Jump、Jump→Idleへの遷移を作成します。
Idle→JumpはConditionsにJumpを設定し、Has Exit Timeのチェックを外します。
Walk→JumpはJumpを設定し、Has Exit Timeのチェックを外します。
Jump→IdleはIsGroundedがtrueを設定し、Has Exit Timeのチェックを外し、SettingsのInterruption SourceをNext Stateにします。
次はJumpブレンドツリーの中身を作成していきます。
Jumpブレンドツリーをダブルクリックします。
Blend Treeを選択し、インスペクタのMotionで+を押しAdd Motion Fieldを選択して、3つのモーションを設定出来るようにします。
また3つのモーションを切り替えるのに使用するのはJumpPowerアニメーションパラメーターなので設定しておきます。
3つのモーションにはUnityのスタンダードアセット(アセットストアからダウンロード&インポートしてください)にあるHumanoidFall、HumanoidMidAir、HumanoidJumpUpアニメーションを設定します。
アニメーションパラメーターのJumpPowerに値に応じてこの3つのモーションをブレンドするのでThresholdにしきい値を設定します。
JumpPowerの値はジャンプした直後に最大値、最高点で0、ジャンプから落下でマイナスの値を入れていきます。
上のような感じです。
なのでジャンプ直後はHumanoidJumpUpアニメーションに近く、最高到達点ではHumanoidMidAirに近く、落下から着地まではHumanoidFallアニメーションに近いものがブレンドさせ再生されます。
今回の場合は-5、0、5という値を設定していますが、これはスクリプトからアニメーションパラメーターのJumpPowerに送る値に応じて変更する必要が出てきます。
これでアニメーターコントローラーが出来ました。
ヒエラルキーのキャラクターを選択し、インスペクタのAnimatorにJumpCharacterアニメーターコントローラーを設定します。
キャラクター操作スクリプトの作成
キャラクターのアニメーターコントローラーが出来たので、次はキャラクターを操作するスクリプトの作成を行います。
新しくJumpCharacterという名前のスクリプトを作成し、キャラクターに取り付けます。
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class JumpCharacter : MonoBehaviour { private CharacterController characterControrller; private Animator animator; // 速度 private Vector3 velocity; // 移動スピード [SerializeField] private float speed = 2f; // ジャンプ力 [SerializeField] private float jumpPower = 6f; // 現在のジャンプ値 private float currentJumpValue; // 2段階目のジャンプ力 [SerializeField] private float doubleJumpPower = 6f; // 最初のジャンプをしているかどうか [SerializeField] private bool isFirstJump; // Start is called before the first frame update void Start() { characterControrller = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); } // Update is called once per frame void Update() { // 入力値の計算 var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 接地時の処理 if(characterControrller.isGrounded) { // 接地時に地面と離れるのを避ける為下向きの力を入れる velocity = Vector3.down; animator.SetBool("IsGrounded", true); // ジャンプ if(Input.GetButtonDown("Jump")) { isFirstJump = true; currentJumpValue = jumpPower; velocity.y = jumpPower; animator.SetTrigger("Jump"); animator.SetBool("IsGrounded", false); } // 二段階ジャンプ } else if(isFirstJump && Input.GetButtonDown("Jump") ) { isFirstJump = false; currentJumpValue = doubleJumpPower; velocity.y = doubleJumpPower; } // 入力値があれば移動値に重力加速度を足して速度を計算 if (input.magnitude > 0f) { transform.LookAt(transform.position + input.normalized); velocity = transform.forward * speed + new Vector3(0f, velocity.y, 0f); animator.SetFloat("Speed", input.magnitude); } else { animator.SetFloat("Speed", 0f); } // ジャンプ位置でアニメーションを変更する為の処理 if (currentJumpValue >= -jumpPower) { currentJumpValue += Physics.gravity.y * Time.deltaTime; animator.SetFloat("JumpPower", currentJumpValue); } // 重力の計算 velocity.y += Physics.gravity.y * Time.deltaTime; characterControrller.Move(velocity * Time.deltaTime); } } |
jumpPowerはインスペクタで設定出来るようにしています。
currentJumpValueはJumpPowerアニメーションパラメーターに送る値です。
doubleJumpPowerは二段階目のジャンプをする時の力です。
isFirstJumpは最初のジャンプをしている最中かどうかです。SerializeFieldアトリビュートが付いているのはUnity実行中に確認するためで、付けなくてもいいです。
Startメソッドで自身からCharacterControllerとAnimatorコンポーネントを取得します。
Updateメソッドでは最初にinputに移動値を入れます。
キャラクターが接地している場合はvelocityにVector3.downを入れます。
これは接地と判断した時に地面とすぐに離れて接地していないとすぐ判定されるのを避ける為に下向きの速度を足しておきます(坂を下る時とかにジャンプが出来ない現象が発生するのを避ける為)。
アニメーションパラメーターのIsGroundedにtrueを入れます。
ジャンプボタンを押したらisFirstJumpをtrueにし、currentJumpValueにjumpPowerを入れます。
さらにvelocityのyにjumpPowerを入れます。
アニメーションパラメーターのJumpをトリガーし、IsGroundedをfalseにします。
キャラクターが接地していない時でisFirstJumpがtrue(1回目のジャンプ中)でジャンプボタンを押した時はisFirstJumpをfalseにし、currentJumpValueにdoubleJumpPowerを入れます。
velocityのyにdoubleJumpPowerを入れます。
アニメーションパラメーターのJumpは既に1回目のジャンプ中なのでトリガーしません。
移動キーを押していればキャラクターの向きをそちらに向けます。
速度にはキャラクターの向いている向きにspeedをかけ、さらにY方向の速度を足した値にします。
これはジャンプ中もキャラクターが左右と奥と手前に移動可とする為の処理で、移動と落下を別に計算し、それを足しています。
アニメーションパラメーターのSpeedにinput.magnitude(ベクトルの長さ)を入れます。
入力値がなければSpeedには0を入れます。
アニメーションパラメーターのJumpPowerに渡す値はジャンプ直後は最大値で時間と共に減らしていく必要があります。
そこでcurrentJumpPowerがマイナスのjumpPowerより大きい時はcurrentJumpPowerを減らしていきます。
減らす値は重力加速度です。
計算した値をアニメーションパラメーターのJumpPowerに渡しています。
今回はフィールドのJumpPowerは6に設定しているので、ジャンプ直後はcurrentJumpValueは6になり、そこからマイナスのJumpPowerである-6より下になるまで-9.81×Time.deltaTimeを足して減らしていきます。
最後に重力値をvelocityのyに足し、最後にCharacterControllerのMoveメソッドを使ってキャラクターを移動させています。
これで機能が出来ました。
キャラクターのインスペクタは以下のような感じになっています。
終わりに
今回の記事は『シンプルなアクションゲームを作ってみよう』カテゴリで作ったジャンプ機能をCharacterControllerコンポーネントに置き換えただけです。
多少スクリプトが変わっているかもしれませんが・・・・。