今回は、前々回作成した移動のプログラム、前回作成したジャンプのプログラムにアニメーションを付けていきます。AnimationとAnimatorというUnityの機能を使用して実装していきます。
アニメーションファイルの作り方
Sprite Editor編では、Idle時のアニメーションファイルを作成しましたが、新たにRunとJumpのアニメーションファイルを作成します。
「Window > Animation > Animation」をクリックして、Animationウィンドウを開きます。
Hierarchyウィンドウで、「Player」のオブジェクトを選択してください。Idleのアニメーションを見ることができると思います。アニメーションを細かく設定したい時はここで設定しますので、操作を覚えていきましょう。
走るアニメーション
Animationウィンドウで「Player Idle Animation > Create New Clip」をクリックします。
保存する場所を決めるダイアログが開きますので、「Assets > Animations」に「Player Run Animation」というファイル名で保存します。
走るアニメーションを作っていきましょう。「Assets > BayatGames > Free Platform Game Assets > Character > Character Animation ( Update 1.8 ) > Run > 1x」の画像の「1x_0~1x_17」まで選択します。複数選択はShift + 左クリックでできます。
複数選択をした状態で、Animationウィンドウにドラッグ&ドロップします。すると、ひし形の図形が複数出現したと思います。
この図形はキーフレームといい、キーフレームがある秒数で設定したプロパティの値に変化する仕組みになっています。
複数画像をドラッグ&ドロップしたことで、1フレーム毎にSpriteのキーフレームを挿入できています。
Player:Spriteの左にある「▼」ボタンをクリックすると、挿入した画像を見ることができます。
ジャンプするアニメーション
ジャンプには、飛ぶ動作と落ちる動作があります。よって、飛ぶアニメーションファイルと落ちるアニメーションファイル2つを作成する必要があります。
初めに、飛ぶアニメーションファイルを作成します。走るアニメーションファイルを作成した時と同様の手順で、「Player Jump Animation」というファイルを作成してください。
そうしたら、「Assets > BayatGames > Free Platform Game Assets > Character > Character Animation ( Update 1.8 ) > Jump > 1x」の画像の1x_0を選択して、アニメーションウィンドウにドラッグ&ドロップします。
走るアニメーションのように複数選択しなかった理由は、飛ぶアニメーションの画像は一枚のみだからです。
アニメーションウィンドウで確認してみましょう。
飛ぶアニメーションのキーフレームが挿入できました。
次に、落ちるアニメーションファイルを作成します。飛ぶアニメーションファイルを作成したときと同様の手順で作成してください。
ファイル名は「Player Fall Animation」、キーフレームのプロパティに設定する画像は「Assets > BayatGames > Free Platform Game Assets > Character > Character Animation ( Update 1.8 ) > Jump > 1x」の1x_1です。
Animatorの使い方
アニメーションの切り替えを行うためには、使用するアニメーションを条件によって分岐させる必要があります。そこで、Unityの機能であるAnimatorを使用します。
「Window > Animation > Animator」をクリックして、Animatorウィンドウを開きます。
Animatorウィンドウを開くと、作成したRunやJumpのアニメーションファイルが表示されます。これらはStateと呼ばれています。
もし、表示されない場合は別のAnimator Controllerを選択している可能性があります。Hierarchyウィンドウで「Player」のオブジェクトを選択してください。
アニメーションの遷移はEntryから開始します。「Player Idle Animation」はLayer Default Stateという、一番初めに遷移するアニメーションに設定されています。そのため、Entryから矢印でつながっており、オレンジ色になっています。
Stateをクリックすると、インスペクターでアニメーションの設定ができます。
「Player Run Animation」の再生速度をSpeedの値を「0.5」変更して調節しました。
Transitionの作り方
アニメーションの遷移をさせるために、Transitionを作成しましょう。
「Player Idle Animation」から「Player Run Animation」のTransitionの作成を例にすると、「Player Idle Animation」のStateを右クリックして「Make Transition」を選択した後、「Player Run Animation」をクリックします。
以下の画像と同じになるようにTransitionの作成を繰り返し行ってください。
StateによってTransitionを作成したりしなかったりしているため、困惑するかもしれません。説明すると、起こり得る条件のみTransitionを作成しているからです。
飛んだ後に走ることや、落下した後に飛ぶことはないので遷移を一方通行にしています。
パラメーターの追加方法
条件を分岐するために、パラメーターを追加します。Float・Int・Bool・Triggerの四種類の型を選択できますが、ここではBool型を使用します。
パラメーター名を入力します。
移動中・ジャンプ中・落下中の判定をしたいので、IsMoving・IsJumping・IsFallingの3つの名前のパラメーターを追加してください。
遷移条件の設定方法
Stateが遷移する条件の設定をします。条件には、追加したパラメーターを使用します。
Transitionを示す矢印を選択した後、インスペクターでConditionsを設定します。右下の「+」ボタンをクリックすることで条件を追加できます。
条件は以下のように設定します。
- 「Player Idle Animation」→「Player Run Animation」は「IsMoving」を「true」に
- 「Player Idle Animation」→「Player Jump Animation」は「IsJumping」を「true」に
- 「Player Idle Animation」→「Player Fall Animation」は「IsFalling」を「true」に
- 「Player Run Animation」→「Player Idle Animation」は「IsMoving」を「false」に
- 「Player Run Animation」→「Player Jump Animation」は「IsJumping」を「true」に
- 「Player Run Animation」→「Player Fall Animation」は「IsFalling」を「true」に
- 「Player Jump Animation」→「Player Fall Animation」は「IsFalling」を「true」に
- 「Player Fall Animation」→「Player Idle Animation」は「IsMoving」を「false」と「IsJumping」を「false」と「IsFalling」を「false」に
- 「Player Fall Animation」→「Player Run Animation」は「IsMoving」を「true」と「IsJumping」を「false」と「IsFalling」を「false」に
「Player Fall Animation」からの遷移の場合の条件を複数指定しているのは、ジャンプ後は止まっている場合と動いている場合があり、分岐させる必要があるためです。
「Has Exit Time」と「Fixed Duration」の設定
初期設定では、アニメーションが最後のフレームまで到達した際にStateが遷移するようになっています。また、遷移にかかる時間は0.25秒と徐々に切り替わるようになっております。
この状態だと、画像を切り替える際に遅延が発生してしまいます。
「Has Exit Time」と「Fixed Duration」の設定をして、画像がすぐに切り替わるようにしていきましょう。
この設定は、条件を追加する時と同じ画面で行えます。
「Has Exit Time」のチェックを外して、アニメションの再生中でも遷移できるように設定しましょう。また、「Fixed Duration」の値を「0」に変更して、一瞬で遷移するように設定しましょう。
スクリプトの追加
スクリプトを追加して、アニメーションが切り替わるように制御します。
Animatorのパラメーターを設定
Animatorのパラメーターの値を変更するには、スクリプトから設定する必要があります。PlayerControllerに処理を追加しましたので、解説していきます。
追加したプログラムは、7行目のAnimatorのコンポーネントを必要とする定義、11行目のanimator変数、16行目のisMoving変数、18行目のisFalling変数、22行目のキーボードの入力情報をhorizontal変数に代入する処理、24行目の移動中の情報を代入する処理、25行目の落下中の情報を代入する処理、34~36行目のanimatorのパラメーターを設定する処理です。
また、27行目のジャンプ条件を「!(rb.velocity.y < -0.5f)」から「!isFalling」に変更し32行目のキーボード入力を「Input.GetAxis(“Horizontal”)」から「horizontal」に変更しました。
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(CapsuleCollider2D))] [RequireComponent(typeof(Animator))] public class PlayerController : MonoBehaviour { [SerializeField] private Rigidbody2D rb; [SerializeField] private Animator animator; [SerializeField] private int moveSpeed; [SerializeField] private int jumpForce; private bool isMoving = false; private bool isJumping = false; private bool isFalling = false; void Update() { float horizontal = Input.GetAxis("Horizontal"); isMoving = horizontal != 0; isFalling = rb.velocity.y < -0.5f; if (Input.GetKeyDown(KeyCode.Space) && !isJumping && !isFalling) { Jump(); } rb.velocity = new Vector2(horizontal * moveSpeed, rb.velocity.y); animator.SetBool("IsMoving", isMoving); animator.SetBool("IsJumping", isJumping); animator.SetBool("IsFalling", isFalling); } void Jump() { isJumping = true; rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Stage")) { isJumping = false; } } }
一行一行解説します。
7行目は、スクリプトでAnimatorのコンポーネントを必要とする定義をしています。省略しても構いませんが、コンポーネントのつけ忘れを防ぎます。
11行目は、animator変数はインスペクターからAnimatorを設定できるように[SerializeField]をつけています。
16行目のisMoving変数は移動中かどうかの情報を保持し、18行目のisFalling変数は落下中かどうかの情報を保持します。
22行目のhorizontal変数の定義と代入は、「Input.GetAxis(“Horizontal”)」の情報をプログラム内で使い回すためです。
同じコードを複数入力してしまうと、一つのコードを書き換えたいときに他の同一コードも書き換える必要が出てきます。その際、書き換え忘れてしまったコードがあるとバグが発生する原因になります。また、情報を取得するまでに時間がかかる処理の場合、パフォーマンスが低下する要因となります。
同じ結果を返す処理の情報は使い回すようにしましょう。
24行目は、isMoving変数に移動中かどうかの情報を取得していて代入しています。horizontal変数の値が「0」の場合はキー入力がされてないことを示します。移動中の際、値は「0」以外となります。
25行目は、isFalling変数に落下中かどうかの情報を取得して代入しています。「rb.velocity.y < -0.5f」はジャンプの判定に使用していました。animatorのパラメーター設定に落下中の情報を使い回したいため、変数を使用しています。
34~36行目はanimatorのパラメーターの設定をしています。bool型のパラメーターを使用しておりますので、SetBool関数を使用します。パラメーター名と値の情報を引数に渡すことで設定できます。
27行目と32行目は、処理結果を使い回すために定義した変数の値を使用するように変更しました。
スクリプトを保存します。その後、プレイヤーのオブジェクトを選択して、インスペクターからanimator変数にAnimatorを設定します。
それでは動作確認をしてみます。プレイボタンをクリックしてください。
しっかりとアニメーションされるようになりましたね。静止時・移動時・ジャンプ時・落下時にアニメーションが切り替わることを確認してください。
しかし、アニメーションがつくことにより、左右の移動に合わせてプレイヤーの向きが変わらないことが気になるようになりました。
走るアニメーションが前方方向に向かって作られていることが原因です。
解決するには、入力によってプレイヤーの向きを変えたり、後ろ走りのアニメーション作成したりする方法があります。今回は前者の方法である、プレイヤーの向きを変える方法を採用します。
プレイヤーの向きを移動方向に合わせる
追加したプログラムは、27行目の移動中の判定、29行目のscale変数の定義とプレイヤーのスケールを代入する処理、31行目のプレイヤーの向きを反転させる際の条件、32行目のscale変数のxの値反転させる処理、36行目のプレイヤーの向きを反転させる処理です。
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(CapsuleCollider2D))] [RequireComponent(typeof(Animator))] public class PlayerController : MonoBehaviour { [SerializeField] private Rigidbody2D rb; [SerializeField] private Animator animator; [SerializeField] private int moveSpeed; [SerializeField] private int jumpForce; private bool isMoving = false; private bool isJumping = false; private bool isFalling = false; void Update() { float horizontal = Input.GetAxis("Horizontal"); isMoving = horizontal != 0; isFalling = rb.velocity.y < -0.5f; if (isMoving) { Vector3 scale = gameObject.transform.localScale; if(horizontal < 0 && scale.x > 0 || horizontal > 0 && scale.x < 0) { scale.x *= -1; } gameObject.transform.localScale = scale; } if (Input.GetKeyDown(KeyCode.Space) && !isJumping && !isFalling) { Jump(); } rb.velocity = new Vector2(horizontal * moveSpeed, rb.velocity.y); animator.SetBool("IsMoving", isMoving); animator.SetBool("IsJumping", isJumping); animator.SetBool("IsFalling", isFalling); } void Jump() { isJumping = true; rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Stage")) { isJumping = false; } } }
それでは解説していきます。
27行目は、移動中の時のみプレイヤーの向きを切り替える処理をしたいため、移動中かどうかの判定をしています。
29行目は、プレイヤーのオブジェクトのスケールの情報を使い回すために、scale変数の定義と代入をしています。ゲームオブジェクトのスケールは、「gameObject.transform.localScale」で取得できます。
31行目は、プレイヤーの向きを反転させる条件です。localScaleの値がマイナスになると、画像が反転します。条件が長いので理解しにくいかもしれませんが、左の入力をしている際にプレイヤーが右をむいているか、右の入力をしている際にプレイヤーが左を向いている場合、真になる条件です。
33行目は、プレイヤーのオブジェクトをx軸で反転させるために、x軸の値にマイナスを掛けています。
36行目は、x軸を反転させた情報を持つscale変数の値を用いて、プレイヤーのオブジェクトのスケールをプログラムから設定しています。
最後にプレイボタンをクリックして、動作確認してみます。
移動する方向に合わせて、プレイヤーの向きが変わるようになりました。
使用アセット一覧
「Unityで2Dアクションゲームの作り方を解説」で紹介・使用するアセットは全て無料のものです。
以下は、現在使用中のアセットです。
次回について
次回は、敵・障害物のプログラムを作成します。