今回はUnityでキャラクターをジャンプさせる機能を搭載してみます。
すでに80記事以上書いていて、今になってやっとジャンプ機能か、という感じですが・・・。
キャラクターを移動させる処理であったり、重力の処理がいまいちうまくいっておらず手を付けていませんでした。
そこら辺の処理が出来てきたので、今回ジャンプする機能をやってみました。
重力に関する記事は
を参考にしてください。
上の重力処理を間違えていると、ジャンプ機能がうまく出来ません。
ジャンプ機能をキャラクター移動スクリプトに追加する
まずは、キャラクター移動スクリプトJumpCharaを作成します。
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 | using UnityEngine; using System.Collections; public class JumpChara : MonoBehaviour { private Animator animator; private CharacterController characterController; // 速度 private Vector3 velocity; // ジャンプ力 [SerializeField] private float jumpPower = 5f; void Start () { animator = GetComponent<Animator>(); characterController = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } // ジャンプキー(デフォルトではSpace)を押したらY軸方向の速度にジャンプ力を足す if(Input.GetButtonDown("Jump")) { velocity.y += jumpPower; } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
地面に接地している時に、方向キーで縦横の動きの処理を入れますが、その後に
1 2 3 | Input.GetButtonDown("Jump") |
で、Jumpに設定されているキーを押した時に、ジャンプさせる処理を入れます。
デフォルトでは、SPACEキーがJumpに設定されているので、SPACEキーを押すとジャンプするようになります。
キーの設定は、UnityのメニューでEdit→Project Settings→Inputで、設定されているキーの確認と変更が出来ます。
スクリプトではvelocity.yの値にjumpPowerを足して、上方向に移動値を加えます。
jumpPowerはインスペクタで値を設定出来るようにしています。
ジャンプキーを検知するのは、characterController.isGroundedがtrueの時、つまり地面に接地している時に行います。
それ以外の所でジャンプキーの検知をすれば、ジャンプ中にさらにジャンプさせ2段ジャンプの機能も作れると思いますが、重力値の計算との兼ね合いで混乱する可能性があります。
2段階ジャンプについては
を参照してください。
これで、SPACEキーを押せばキャラクターがジャンプするようになります。
Unityの実行ボタンを押して確認してみてください。
立っている時と移動している時にSPACEキーを押すと、キャラクターがジャンプします。
移動中にジャンプキー押せば、縦横の移動をしながらジャンプするのがわかると思います。
ジャンプ中はジャンプ用アニメーションを再生する
ここまでの機能だと、ジャンプ中のアニメーションはその時のアニメーションを再生している状態です。
そこで、ジャンプ中はジャンプのアニメーションを再生するように変更します。
上のようにJump状態とアニメーションパラメータにJumpをBool型で作成します。
Idle→JumpへはJumpがtrueになった時に遷移するようにします。
Jumpのアニメーションクリップには、Standard AssetsのJumpを設定します。
このJumpは助走が入っているので、23%左に移動させ、助走部分が入らないようにします。
Jump→Idleへの遷移は、Has Exit Timeのチェックを入れ、条件にJumpがfalseの時を設定します。
これで、Jumpがfalseになってもジャンプアニメーションを最後まで再生するようになります。
着地後に、すぐ別のアニメーションに遷移させたい場合はHas Exit Timeのチェックを外し、スクリプトで着地後すぐアニメーションパラメータのJumpをfalseにします。
ジャンプアニメーションへ遷移させる処理をスクリプトに追加
これでアニメーターコントローラの設定が終わったのでJumpCharaスクリプトにアニメーション操作の処理を加えます。
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 | using UnityEngine; using System.Collections; public class JumpChara : MonoBehaviour { private Animator animator; private CharacterController characterController; // 速度 private Vector3 velocity; // ジャンプ力 [SerializeField] private float jumpPower = 5f; void Start () { animator = GetComponent<Animator>(); characterController = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") ) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } // ジャンプキー(デフォルトではSpace)を押したらY軸方向の速度にジャンプ力を足す if(Input.GetButtonDown("Jump") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") ) { animator.SetBool ("Jump", true); velocity.y += jumpPower; } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
ジャンプキーが押された時に
1 2 3 | animator.SetBool("Jump", true); |
でアニメーションを遷移させます。
条件に
1 2 3 | !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") |
を追加していますが、これは現在ジャンプアニメーションを再生中でない時という意味で、着地後もジャンプアニメーションが再生されているので、それが終わるまでは再度ジャンプしないようにします。
キャラクターの移動処理の部分にも同じ処理が追加されていますが、着地後のジャンプアニメーション中にキャラが移動しないようにする為です。
着地後すぐジャンプアニメーションをやめ、移動できるようにする場合はこの条件はいりません。
これでスクリプトの編集が終わりました。
しかし、このままだとアニメーションパラメータのJumpがfalseになる事がないので、ジャンプのアニメーションの最後で止まってしまいます。
Behaviourを使ってアニメーションパラメータを操作する
せっかくなので以前に作成した記事
を使ってアニメーションパラメータを操作してみます。
アニメーターコントローラーのJumpの状態を選択し、インスペクタでAdd Behaviourをクリックし新しいスクリプトEndJumpを作成します。
現在の所、ビヘイビアはC#でしか作れないみたい?(JavaScriptでも作成出来ました)
ビヘイビアを作成するとデフォルトで以下のような記述がされています。
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 | using UnityEngine; using System.Collections; public class EndJump : 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) { animator.SetBool ("Jump", false); } // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks //override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // 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) { // //} } |
今回は、Jumpに遷移後にアニメーションパラメータのJumpをfalseにしたいので、OnStateEnterのコメント化されている部分を削除し、中に
1 2 3 | animator.SetBool("Jump", false); |
という処理を追加します。
これでJump状態に遷移してすぐにアニメーションパラメータのJumpがfalseになります。
JavaScriptでEndJampビヘイビアスクリプトを作成するにはまず通常のJavaScriptファイルを作成し、StateMachineBehaviourクラスを継承して新しいクラスを作成します。
1 2 3 4 5 6 7 8 9 10 | #pragma strict public class EndJumpJ extends StateMachineBehaviour { function OnStateEnter(animator : Animator, stateInfo : AnimatorStateInfo, layerIndex : int) { animator.SetBool ("Jump", false); } } |
あとは通常のスクリプトのC#とJavaScriptの違いで使い方は同じです。
ビヘイビアはアニメーターコントローラーの状態のインスペクタのAdd Behaviourを押して追加します。
これでジャンプ機能が完成しました。
Unityの実行ボタンを押して確認してみましょう。
今回は、ジャンプのアニメーションが終了するまで操作不可にしてますが、前述したとおり、着地と同時にアニメーション遷移や移動させる事も可能なので、そこら辺は好みに応じてスクリプトを組んでいくといいと思います。
坂のような場所を下っている時に、CharacterControllerのIsGroundedがfalseになり、ジャンプが出来ない事があります。
そんな時は
を参照してください。
下り坂やデコボコな道でIsGroundedで接地を判断出来ない時にも、スクリプトで設置を判断しジャンプさせる事が出来るようになります。
また2段階ジャンプをさせたい場合は
↑も参考にしてください。
ジャンプ中に移動キーで移動させる
ツイッターで質問を頂きましたので、ジャンプ中に移動キーを押した時にキャラクターを移動させる方法を追記します。
ここまでのジャンプ機能では、ジャンプ中は操作できないような仕様にしていたので、ジャンプ中は移動キーを押してもキャラクターは空中で移動出来ませんでした。
しかし、ジャンプ中もキャラクターを動かしたい場合もあると思います。
そんな時は、キャラクターが地面に接地中に移動値を取得するという条件を外してしまいましょう。
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 | using UnityEngine; using System.Collections; public class JumpChara : MonoBehaviour { private Animator animator; private CharacterController characterController; // 速度 private Vector3 velocity; // ジャンプ力 [SerializeField] private float jumpPower = 5f; void Start () { animator = GetComponent<Animator>(); characterController = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { if (characterController.isGrounded) { velocity = Vector3.zero; } var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity.x = input.normalized.x * 2; velocity.z = input.normalized.z * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } // ジャンプキー(デフォルトではSpace)を押したらY軸方向の速度にジャンプ力を足す if(Input.GetButtonDown("Jump") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && !animator.IsInTransition(0) ) { animator.SetBool ("Jump", true); velocity.y += jumpPower; } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
接地した時は、velocityの値を初期化するだけにして、Update関数内で移動値は常に取得するようにします。
また、移動値はX方向とZ方向のvelocityを別に代入するようにします。
今までは接地している時だけvelocityに値を足すという形にしていましたが、そのまま足すという処理のままだとジャンプ中の移動でものすごく速い移動をしてしまいます。
なので、重力値であるvelocity.zの値を書き換えず個別に値を代入しています。
また、ジャンプ中にジャンプキーを押すと、たまにジャンプしてしまう不具合が発生したので
1 2 3 | !animator.IsInTransition(0) |
というアニメーションが遷移途中でないという条件を加えました。
これで、ジャンプ中の移動が出来るようになったので試してみましょう。
↑のようにジャンプ中に移動キーを押すと移動出来るようになりました。
終わりに
今回のジャンプ機能ではアニメーションパラメータのJumpをBool型で作成し、ビヘイビアを使ってJumpをfalseにしています。
ビヘイビアを使うという事も含めてそういう風に作りましたが、ビヘイビアを使わなくても出来ます。
アニメーションパラメータのJumpをTriggerで作成し、Jump→Idleの遷移はHas Exit Timeにチェックを入れるだけで他の条件を入れないようにします。
その他のJumpがtrueの時に遷移していた条件をTriggerのJumpにします。
JumpをTrigger型にしたのでスクリプトでアニメーションパラメータを変更する箇所では、
1 2 3 | animator.SetTrigger("Jump"); |
という風に記述します。
こちらの方がシンプルですね。