今回はジャンプしている最中に再度ジャンプキーを押した時に2段階ジャンプが出来るようにします。
ジャンプ機能については
を参考にしてください。
またジャンプ中にコライダのサイズを変更する場合は
を参考にしてください。
キャラクターはアニメーションの動きで移動やジャンプを反映させるのではなく、スクリプトで操作するキャラクターになります。
2段階ジャンプの機能をどうやって作るか?
今回作成する2段階ジャンプの機能を考えていきましょう。
まずは通常のジャンプを考えます。
ジャンプキーを押したらジャンプアニメーションをさせ、キャラクターの上向きにジャンプ力を足します。
キャラクターには重力を働かせているので数秒後には落下して地面に接地します。
地面に接地したら重力値を0にしています。
2段階ジャンプについて考えましょう。
2段階ジャンプなので、まずはジャンプしている必要があります。
ジャンプ中にジャンプキーを押したらキャラクターの上向きに2段階目のジャンプ力を足します。
ジャンプ中いつでも2段階目のジャンプが出来ると地面スレスレで再度ジャンプするという事が出来てしまいます。
そういう風に作りたい場合はいいんですが、今回の場合はジャンプのアニメーションの再生位置に応じて2段階目のジャンプが出来るようにしておきます。
またジャンプ中に2段階目ジャンプを何度も試みることが出来てしまうので一度二段階ジャンプを試みたらそのジャンプ中には二段階目のジャンプは出来ないようにします。
アニメーターコントローラーではJump状態からDoubleJump状態へと遷移させるようにします。
ジャンプの1段階目、2段階目に関係なく重力でキャラクターは落下し、地面に接地します。
もし2段階目のジャンプをスレスレで出来るようにするとなると重力の事もあるので難しくなります。
2段階目のジャンプ力が重力加速度分の落下力より高くないと一瞬止まるだけのようにしか見えないからです。
アニメーターコントローラーに2段階目のジャンプの状態と遷移を作成する
それではまずアニメーターコントローラーでDoubleJumpの状態の作成と遷移を作成していきましょう。
↑のようにDoubleJump状態を作成します。
DoubleJumpのアニメーションクリップにはJumpと同じアニメーションを設定しました。
Jump→DoubleJump、DoubleJump→Idleへと遷移を繋げます。
EndJumpビヘイビアを使用するのをやめて別の制御を行う
先ほど紹介した記事でジャンプ機能を作成した方はJump状態にEndJumpビヘイビアを設定しアニメーションパラメータのJumpをfalseにしていました。
今回の2段階ジャンプ機能搭載の際にここでJumpをfalseにするとスクリプトの制御が難しくなるので、この機能はオフにしておきます。
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 | 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) { // //} } |
↑のようにコメント化するかEndJump自体を外してください。
DoubleJumpへの遷移条件を設定する
では次にアニメーションパラメータにDoubleJumpを作成します。
Jump→DoubleJumpへの遷移条件にDoubleJumpがtrueの時を設定します。
↑が遷移条件になります。
Has Exit Timeのチェックを外し、DoubleJumpを左に32%動かして最初の方のアニメーションを再生させないようにします。
これはStandardAssetのJumpを使用していてこのアニメーションには助走があるのでカットしています。
他のアニメーションクリップを使用している場合はそれぞれアニメーションの助走部分をカットするか、もしくは必要ありません。
DoubleJump→Idleへの遷移条件は↑のようにします。
アニメーションパラメータのDoubleJumpがfalseになったらIdle状態へ遷移させます。
2段階ジャンプ機能を取りつけたDoubleJumpCharaスクリプトの作成
これでアニメーターコントローラーの設定が終了したので、キャラクターに移動とジャンプ機能、2段階ジャンプ機能を取りつけたDoubleJumpCharaを作成します。
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | using UnityEngine; using System.Collections; public class DoubleJumpChara : MonoBehaviour { private Animator animator; private CharacterController cCon; private Vector3 velocity; // 通常のジャンプ力 [SerializeField] private float jumpPower = 5f; // 2段階目のジャンプ力 [SerializeField] private float doubleJumpPower = 5.6f; // 2段階ジャンプ中かどうか private bool isDoubleJump = false; // 一度2段階ジャンプを試みたかどうか private bool tryDoubleJump; // 歩くスピード [SerializeField] private float walkSpeed = 1.5f; // 移動力 private Vector3 input; void Start() { animator = GetComponent<Animator>(); cCon = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update() { // キャラクターコライダが接地、またはレイが地面に到達している場合 if (cCon.isGrounded) { velocity = Vector3.zero; // 着地していたらアニメーションパラメータと2段階ジャンプフラグをfalse animator.SetBool("Jump", false); animator.SetBool("DoubleJump", false); isDoubleJump = false; tryDoubleJump = false; 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 * walkSpeed; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0); } // ジャンプ if (Input.GetButtonDown("Jump") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && !animator.IsInTransition(0) ) { animator.SetBool("Jump", true); velocity.y += jumpPower; } // 1段階目のジャンプ中 } else if (animator.GetBool("Jump") && !isDoubleJump && !tryDoubleJump ) { input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")).normalized; velocity = new Vector3(input.x * walkSpeed, velocity.y, input.z * walkSpeed); // ジャンプボタンで2段階目のジャンプ if (Input.GetButtonDown("Jump")) { // アニメーション再生位置を取得 var normalizedTime = Mathf.Repeat(animator.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f); Debug.Log(normalizedTime); // アニメーション位置で2段階目のジャンプをするかどうか決める if (animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && 0.35f <= normalizedTime && normalizedTime <= 0.5f) { // 2段階ジャンプの設定 isDoubleJump = true; animator.SetBool("DoubleJump", true); // キーを押した方向にキャラクターを向ける transform.LookAt(transform.position + input.normalized); // Y方向に2段階ジャンプ力を足す velocity.y += doubleJumpPower; } else { // 一度二段階目のジャンプを試みたら地面に着地するまでは出来ないようにする tryDoubleJump = true; } } // 2段階ジャンプ中の処理 } else if (isDoubleJump) { Debug.Log("kita"); input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")).normalized; velocity = new Vector3(input.x * walkSpeed, velocity.y, input.z * walkSpeed); } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } } |
少しづつ見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 | // 通常のジャンプ力 [SerializeField] private float jumpPower = 5f; // 2段階目のジャンプ力 [SerializeField] private float doubleJumpPower = 5.6f; // 2段階ジャンプ中かどうか private bool isDoubleJump = false; // 一度2段階ジャンプを試みたかどうか private bool tryDoubleJump; |
を宣言します。
doubleJumpPowerは2段階目のジャンプ力でインスペクタで設定出来るようにします。
isDoubleJumpは2段階ジャンプ中かどうかのフラグです。
tryDoubleJumpは1回のジャンプ中で二段階目のジャンプを試みたかどうかのフラグです。
一度試みたらそのジャンプ中では二度と二段階目のジャンプをさせないようにします。
1 2 3 4 5 6 7 8 9 10 11 | // キャラクターコライダが接地、またはレイが地面に到達している場合 if (cCon.isGrounded) { velocity = Vector3.zero; // 着地していたらアニメーションパラメータと2段階ジャンプフラグをfalse animator.SetBool("Jump", false); animator.SetBool("DoubleJump", false); isDoubleJump = false; tryDoubleJump = false; |
CharacterControllerのisGroundedプロパティで接地が確認された時にアニメーションパラメータのJumpとDoubleJump、isDoubleJump、tryDoubleJumpをfalseにします。
つまり、地面に着地した時はジャンプ系のパラメータをオフにしています。
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 | // 1段階目のジャンプ中 } else if (animator.GetBool("Jump") && !isDoubleJump && !tryDoubleJump ) { input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")).normalized; velocity = new Vector3(input.x * walkSpeed, velocity.y, input.z * walkSpeed); // ジャンプボタンで2段階目のジャンプ if (Input.GetButtonDown("Jump")) { // アニメーション再生位置を取得 var normalizedTime = Mathf.Repeat(animator.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f); Debug.Log(normalizedTime); // アニメーション位置で2段階目のジャンプをするかどうか決める if (animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && 0.35f <= normalizedTime && normalizedTime <= 0.5f) { // 2段階ジャンプの設定 isDoubleJump = true; animator.SetBool("DoubleJump", true); // キーを押した方向にキャラクターを向ける transform.LookAt(transform.position + input.normalized); // Y方向に2段階ジャンプ力を足す velocity.y += doubleJumpPower; } else { // 一度二段階目のジャンプを試みたら地面に着地するまでは出来ないようにする tryDoubleJump = true; } } |
ジャンプ中で二段階目のジャンプでなく、二段階目のジャンプを試みていない時に二段階目のジャンプをするかどうかの判定をします。
ジャンプ中でも移動キーを押したら移動出来るようにしています。
ジャンプボタンを押した時にアニメーションの再生位置を調べています。
1 2 3 | var normalizedTime = Mathf.Repeat(animator.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f); |
でアニメーターコントローラーで再生中のアニメーションの再生位置を取得し0~1内で納めます。
アニメーターコントローラーの状態がJumpで、そのアニメーションの再生位置が0.35以上0.5以下の時に二段階ジャンプが出来るようにしています。
ジャンプキーを押した時で再生位置等によって二段階ジャンプの条件に漏れた場合はtryDoubleJumpをtrueにして次にキーを押しても二段階目のジャンプは出来ないようにします。
1 2 3 4 5 6 7 | // 2段階ジャンプ中の処理 } else if(isDoubleJump) { input = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")).normalized; velocity = new Vector3(input.x * walkSpeed, velocity.y, input.z * walkSpeed); } |
2段階ジャンプ中に移動キーで位置を変更出来るようにする場合はキーの入力値を取得してvelocityに値を足します。
Y軸の値は重力加速度をそのまま設定します。
1段階目のジャンプ中も2段階目のジャンプ中もジャンプ中に前後左右に動けるようにしてます。
2段階ジャンプが出来るかどうか確認する
これで2段階ジャンプの機能が完成です。
キャラクターにDoubleJumpCharaスクリプトを取りつけインスペクタにパラメータを設定してください。
↑のようなパラメータを設定しました。
それではUnityの実行ボタンを押して確認してみましょう。
↑のようになりました。
2段階ジャンプが出来るようになりました!
ジャンプ力を上げるとアニメーション再生位置とジャンプしている高さが合わなくなってくるので調整が必要です。
ジャンプして着地後すぐにジャンプさせたい場合
2段階ジャンプとは関係ないですが、一度ジャンプをして着地したと同時にジャンプキーで再度ジャンプさせたい場合もあるかと思います(連続でジャンプ)。
そういった時は
1 2 3 4 5 6 7 8 | // ジャンプ if(Input.GetButtonDown("Jump") // && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") // && !animator.IsInTransition(0) // 遷移途中にジャンプさせない条件 ) { |
↑のように状態がJump中でないという条件とアニメーションが遷移途中でないという条件をコメント化するか削除してください。
すると、連続ジャンプが出来ます(試しに連打してみてください)。
ただ、着地と同時にジャンプ出来るので、アニメーションの遷移よりもスクリプトのY座標の値の更新が早い為、歩いている時にキャラクターが浮きあがるという感じになります。
これが気になる人はジャンプ時のアニメーションを使わないようにすると違和感がなくなると思います。
↑が連続ジャンプと方向転換の実行結果です。
連続でジャンプ出来ていますね!