UnityのStandardAssetsの中にあるFPSControllerのキャラクター移動のスクリプトを使うとShiftキーを押しながら移動キーを押すと走る事が出来きます。
(ThirdPersonControllerの場合は逆にShiftキーを押しながら移動キーを押すと歩きになります)
しかしFPSControllerのようにShiftキーを押して移動キーを押すと走るのではなく、移動キーを素早く2回押しした時に走るようにすればキャラクターの操作がより簡潔になり、攻撃キーとの組み合わせもしやすくなります。
例えば移動キーはキーボードの↑↓←→だけを使って歩く、走る動作をさせ、Shiftキーは物を投げる攻撃をさせたりといった感じです。
そこで今回は移動キーを素早く2回押しする事で歩きではなく走る事が出来るような機能を作成していきたいと思います。
キャラクターの準備
まずは今回の機能を作成する前にキャラクターの準備をしておきます。
キャラクターの準備
移動させるキャラクターモデルはAssets→StandardAssets→Characters→ThirdPersonCharacter→Models→Ethanをヒエラルキーにドラッグ&ドロップし、CharacterControllerを取りつけコライダのサイズを調整します。
AnimatorControllerを新しく作成しキャラクターのインスペクタのAnimatorに設定します。
Animatorは
↑のようにアニメーションパラメータのSpeed(Float)とRun(bool)を作成します。
Idle→WalkはSpeedが0.1以上、SettingsのInterrupt SourceにNext State
Walk→IdleはSpeedが0.1以下
Idle→RunはSpeedが0.1以上、Runがtrue
Walk→RunはRunがtrue
Run→WalkはRunがfalse
という条件を付けて遷移するようにします。
全ての遷移でHas Exit Timeのチェックを外しておきます。
Idle→WalkのSettingsのInterrupt SourceにNext Stateを設定したのは次の条件が成立した時に素早くアニメーションを遷移させる為です。
例えばIdle→Walkの条件が成立して遷移している途中でアニメーションパラメータのRunが有効になりすぐにRun状態に遷移させたいけどWalk状態に行ってからRun状態へ遷移する為、途中でWalkアニメーションが流れてしまいます。
そんな時にInterrupt SourceにNext Stateを設定しておくと次の条件が成立している時はWalk状態からすぐにRun状態へと遷移してくれます。
AnimatorControllerの設定等は
等を参照してください。
キャラクター移動スクリプトの準備
基本的なキャラクターを移動させるスクリプトを準備しておきます。
スクリプトの名前はNormalMoveとしていますが、ご自由に変えてください。
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 | using UnityEngine; using System.Collections; public class NormalMove : MonoBehaviour { private CharacterController characterController; private Vector3 velocity; private Animator animator; public float walkSpeed = 1.25f; // 歩くスピード // Use this for initialization void Start() { characterController = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); } // Update is called once per frame void Update() { if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); if (input.magnitude >= 0.1f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity = transform.forward * walkSpeed; } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
このスクリプトを先ほどのキャラクターに取り付ければ簡単な移動をしてくれます。
今回の機能はこのスクリプトに追記していく形で作成したいと思います。
キーの素早い2回押しで走る為の概要
今回の機能を作成する前にキーの2回押しをどうやって判定するか考えてみようと思います。
移動キーが最初に押された時に押された!という情報を保持しておき、次に移動キーが押されるまでの時間を計測しその時間が決められた時間内であれば
素早く2回移動キーが押されたと判定し、走るようにすればいいと思います。
これだけであれば単純にいきそうですが、実装しようと思うといくつかの処理を加える必要がありそうです。
最初に移動キーを押してから離した場合は『押された!』という情報を破棄したいですが、次の移動キーを押す時に必ず離しますので単純に『押された!』という情報を捨てる事が出来ません。
つまり最初に移動キーを押してから離した時には『押された!』という情報を破棄しないで次にキーを押されるまでは保持しておく必要があります。
『押された!』という情報を破棄するタイミングはキャラクターが『走っている』という状態で移動キーを押すのをやめた時に実行するのが良さそうです。
『押された!』という情報を保持している間は次に移動キーが押されるまでの時間を計測し続け、決められた時間を越えた時に『押された!』という情報を破棄します。
また、素早く移動キーを2回押した時に単純に走るようにしてもいいんですが、この場合移動キーの右を押した後に左を押した場合も走るという事になってしまいます。
つまり素早くキーを押した場合でも移動する方向が全然違うキーを押しても走ってしまいます。
こういう仕様でいいというならいいんですが、最初に押した方向と次に押した方向から角度を調べ制限角度内のキー押しであれば走るようにすればこういった事が起きなくなります。
移動キーを押した方向がまったく同じというのではなかなか走れなくなるので制限角度を決めてある程度許容するようにします。
移動キーの素早い2回押しで走るキャラクタースクリプトを実装する
移動キーの素早い2回押しで走る機能を作成する考え方がわかったところでそれをスクリプトで記述してみましょう。
まずはフィールドを追加します。
1 2 3 4 5 6 7 8 9 10 | public float dashSpeed = 3f; // 走るスピード public bool run = false; // 走っているかどうか public bool push = false; // 最初に移動ボタンを押したかどうか public float nextButtonDownTime = 0.3f; // 次に移動ボタンが押されるまでの時間 private float nowTime = 0f; // 最初に移動ボタンが押されてからの経過時間 public float limitAngle = 3f; // 最初に押した方向との違いの限度角度 private Vector2 direction = Vector2.zero; // 移動キーの押した方向 |
先ほどの概要で出てきた『押された!』という情報はpushです。
pushがtrueになった時時間計測のnowTimeを0に初期化し時間計測をしていくことになります。
limitAngleは最初に押した方向と次に押した方向から計算した角度の限界値を指定します。
この値以下の時は走るようにします。
directionは最初に移動キーを押した時のその方向を入れておくフィールドです。
2回目に移動キーを押した時の方向とこのdirectionから角度を求め走るかどうかを決めます。
次に走るかどうかの処理をNormalMoveに追記します。
Updateメソッド内の
velocity = Vector3.zero;
と
var input = new Vector3(Input.GetAxis(“Horizontal”), 0, Input.GetAxis(“Vertical”));
の間に追記します。
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 | // 走っていない時 if (!run) { // 移動キーを押した if ((Input.GetButtonDown ("Horizontal") || Input.GetButtonDown ("Vertical"))) { // 最初に1回押していない時は押した事にする if (!push) { push = true; // 最初に移動キーを押した時にその方向ベクトルを取得 direction = new Vector2(Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical")); nowTime = 0f; // 2回目のボタンだったら1→2までの制限時間内だったら走る } else { // 2回目に移動キーを押した時の方向ベクトルを取得 var nowDirection = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw ("Vertical")); // 確認の為、最初に押した方向と2回目に押した方向の角度をコンソールに出力 Debug.Log (Vector2.Angle (nowDirection, direction)); // 押した方向がリミットの角度を越えていない かつ 制限時間内に移動キーが押されていれば走る if (Vector2.Angle (nowDirection, direction) < limitAngle && nowTime <= nextButtonDownTime) { run = true; animator.SetBool ("Run", true); } } } // 走っている時にキーを押すのをやめたら走るのをやめる } else { if (!Input.GetButton ("Horizontal") && !Input.GetButton ("Vertical")) { run = false; push = false; animator.SetBool ("Run", false); } } // 最初の移動キーを押していれば時間計測 if (push) { // 時間計測 nowTime += Time.deltaTime; if (nowTime > nextButtonDownTime) { push = false; } } |
まずは走っていない時(!run)にInput.GetButtonDown(“Horizontal”)、Input.GetButtonDown(“Vertical”)を使って移動キーが押されているかどうかを判定します。
pushがfalseの時にpushをtrueにし最初の移動キーが押されたことにします。
Input.GetAxisRawを使って平滑化した移動値を計算しdirectionに代入しています。
Input.GetAxisRawを使用するとキーボードを押した時に-1、0、1の値が得られる為に使用しています。
キーボード以外の場合はそれ以外の値も入ってくるので、もし-1、0、1の値しか取らないようにするには
を参照してください。
else以下はすでにpushがtrueになっており1回目の移動キーが押されていた時の処理です。
2回目に押した方向をnowDirectionに入れ、Vector2.Angleを使って最初に押した方向(direction)との角度を計算し、
limitAngle以下であり、キーを押すまでにかかった時間との比較をして走るかどうかを決めています。
移動方向はVector3で計算してもいいんですが、今回の場合Y座標は使っていないのでVector2で計算しています。
if(!run) {
のelseの処理は走っている時なので、走っている時に移動キーが押されていない時は走るのをやめさせています。
pushがtrueの時は時間計測をし、決められた時間を越えたらpushをfalseにして最初に移動キーが押された事を無効化しています。
最後にこれらの処理の後に移動値を計算する処理が入りますが、それを改造します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | input = new Vector3 (Input.GetAxis ("Horizontal"), 0, Input.GetAxis ("Vertical")); if (input.magnitude >= 0.1f) { animator.SetFloat ("Speed", input.magnitude); transform.LookAt (transform.position + input); // 走っていたら移動値をdashSpeedにする if (run) { velocity = transform.forward * dashSpeed; } else { velocity = transform.forward * walkSpeed; } } else { animator.SetFloat ("Speed", 0f); } |
走っているか走っていないかで移動値を変更しています。
ここは説明がいらないぐらいシンプルですね。
これで移動キーの素早い2回押しで走る機能が出来上がりました。
素早く2回押しで走るか確認する
機能が完成したので、キャラクターにスクリプトを設定し動きを確認してみましょう。
↑のようにインスペクタで設定をしました。
今回は移動キーの方向の角度を3度とかなり厳しめな設定をしています。
本来、RunやPushはインスペクタに表示する必要はないですが、どのようにオン・オフされるか確認する為に表示しています。
ちょっと動画だけではキー操作が見えないのでわかり辛いですが、機能は問題なさそうです。
limitAngleの角度を調整すれば移動キーの方向の許容範囲を広げる事が出来るので90度以内は走る動作にするといったことも可能ですね。