Unity初心者が2Dタワーディフェンスを作っています。今回は敵の移動方向に応じてアニメーションを切り替える処理を実装します。
- Mac mini (M1, 2020)
- Unity 2022.3.36f1
左、上、下方向のアニメーションの作成
敵が右方向へ移動するときのアニメーションは前回作成済みです。同じ手順で左、上、下方向へ移動するときのアニメーションを作成します。
なお、以下のようなキャラチップをインポート済みで、これらを使ってアニメーションを作成します。
- 敵画像のslime_leftとslime_walk_leftをまとめて選択してヒエラルキービューにドラッグ&ドロップします。
- 「Create New Animation」ウインドウが開くので「Assets/Animations」フォルダ下に名前を「Enemy0_left」にして保存します。
- ヒエラルキーに「slime_left」という名前のオブジェクトが作成されるので、インスペクターの「Sorting Layer」を「Object」に変更します。これでオブジェクトが最前面に表示されます。
- このオブジェクトのAnimationビューで「Samples」の値を2に変更します。これはアニメーションの速度の設定です。再生ボタンを押してアニメーションを確認しましょう。
- ヒエラルキーのオブジェクト「slime_left」は削除します。アニメを作成するために作ったものなので、もう用済みです。
以上の手順で上方向のアニメ(slime_up)と下方向のアニメ(slime_down)も作成します。
「Assets/Animations」フォルダに作成されたAnimatorはひとつを残して削除します。
条件に合わせて移動アニメーションを切り替える
Blend Treeという機能を使って、移動アニメーションを切り替えます。Blend Treeとは複数のアニメーションを滑らかに切り替えるための仕組みです。
ヒエラルキーの「Enemy」オブジェクトを選択した状態で、メニューバーの「Window > Animation > Animator」を選択します。
「Animator」ウインドウが開くので、どこか適当なところにドラッグしてタブ化しましょう。
Animatorビューの「Parameters」タブで「+」をクリックし「Float」を選択します。浮動小数点数の値を持つパラメーターが追加されるので名前を「X」にします。
もう一度同じ手順で「Float」を選択し、名前が「Y」のパラメーターを追加します。
Animatorビューの何もないところでで右クリック「Create State > From New Blend Tree」を選択します。
「Blend Tree」という名前のステートが作成されるので、インスペクターで名前を「Walk」に変更します。
Animatorビューの「Entry」ステートの上で右クリック「Set StateMachine Default State」を選択して、矢印を「Walk」につなげます。
「Enemy0_right」ステートは削除しちゃってOKです。
「Walk」ステートをダブルクリックするとBlend Treeの設定画面に切り替わります。
「Brend Tree」ステートをクリックするとインスペクターの表示が切り替わるので、「Blend Type」を「2D Simple Directional」に変更します。
「Parameters」の右側を「Y」に変更します。
Animatorビューの「Blend Tree」ステートにもXとYのパラメーターが表示されました。
インスペクターの「Motion」にアニメーションを登録して再生するための条件を設定します。
「Motion」の「+」をクリックして「Add Motion Field」を選択します。
まずは右方向へ移動するときのアニメーションを登録します。「None (Motion)」をクリックして「Enemy0_right」を選択します。「Pos X」を1に「Pos Y」を0に設定します。
同じ手順で、左、上、下方向へ移動するときのアニメーションも設定します。
Motion | Pos X | Pos Y |
---|---|---|
Enemy0_right | 1 | 0 |
Enemy0_left | -1 | 0 |
Enemy0_up | 0 | 1 |
Enemy0_down | 0 | -1 |
Animatorビューはこんな感じになりました。
動作確認をします。
インスペクターの下の方にある「Blend Tree」の再生ボタンを押します。「Parameters」の赤いポッチをドラッグするとアニメーションが切り替わります。
スクリプトでアニメーションを切り替える
EnemyControllerスクリプトを修正して、敵の移動方向を取得しアニメを切り替えます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening; // DOTweenを使うために必要な宣言
using System.Linq; // Linqを使うために必要な宣言
public class EnemyController : MonoBehaviour
{
[SerializeField, Header("移動経路の情報")]
private PathData pathData;
[SerializeField, Header("移動速度")]
private float speed;
private Vector3[] path; // pathDataから取得した座標を格納するための配列
private Animator animator; // Animatorコンポーネントの取得
void Start()
{
// Animatorコンポーネントを取得して代入
TryGetComponent(out animator);
// 経路を取得
path = pathData.pathArray.Select(x => x.position).ToArray();
// 経路の総距離を計算
float totalDistance = CalculatePathLength(path);
// 移動時間を計算 (距離 ÷ 速度)
float moveDuration = totalDistance / speed;
// 経路に沿って移動
// transform.DOPath(path, moveDuration).SetEase(Ease.Linear); (以下に修正)
transform.DOPath(path, moveDuration)
.SetEase(Ease.Linear)
.OnWaypointChange(x => ChangeWalkingAnimation(x));
}
/// <summary>
/// 経路の総距離を計算
/// </summary>
/// <param name="path">経路座標の配列</param>
/// <returns>経路の総距離</returns>
private float CalculatePathLength(Vector3[] path)
{
float length = 0f;
for (int i = 0; i < path.Length - 1; i++)
{
// 各セグメントの距離を計算して合計
length += Vector3.Distance(path[i], path[i + 1]);
}
return length;
}
/// <summary>
/// 敵の進行方向を取得してアニメを変更
/// </summary>
private void ChangeWalkingAnimation(int index)
{
// 次の移動先がない場合は処理を終了
if (index >= path.Length)
{
return;
}
// 左方向
if (transform.position.x > path[index].x)
{
animator.SetFloat("Y", 0f);
animator.SetFloat("X", -1.0f);
}
// 上方向
else if (transform.position.y < path[index].y)
{
animator.SetFloat("X", 0f);
animator.SetFloat("Y", 1.0f);
}
// 下方向
else if (transform.position.y > path[index].y)
{
animator.SetFloat("X", 0f);
animator.SetFloat("Y", -1.0f);
}
// 右方向
else
{
animator.SetFloat("Y", 0f);
animator.SetFloat("X", 1.0f);
}
}
}
31行目、DOTweenのOnWaypointChange()
メソッドを追加しました。このメソッドは経路として指定した座標に到達するたびに呼び出されます。つまり、経路の曲がり角に到達するたびにChangeWalkingAnimation()
関数が実行されます。引数には移動経路のインデックスが渡されます。
ChangeWalkingAnimation()
関数は、敵の移動方向に応じてアニメーターのX
とY
に値をセットします。
動作確認しましょう。
移動方向に応じてスライムの向きが変わりました。いえい。
コードのリファクタリング
ChangeWalkingAnimation()
関数がなんだか冗長だなぁと思いませんか。私は思います。
というわけで、ChatGPT氏にリファクタリングをお願いしました。
/// <summary>
/// 敵の進行方向を取得してアニメを変更
/// </summary>
private void ChangeWalkingAnimation(int index)
{
// 次の移動先がない場合は処理を終了
if (index >= path.Length)
{
return;
}
// 移動先の方向を計算
Vector2 direction = (path[index] - transform.position).normalized;
// XとY方向をアニメーターに設定
animator.SetFloat("X", Mathf.Round(direction.x));
animator.SetFloat("Y", Mathf.Round(direction.y));
}
はい、めっちゃシンプルになりましたね。自力で書けと言われても絶対に書けないコードです。引き算で移動方向を計算できるんですね。なぜ?
オブジェクトをプレハブ化する
ゲーム内に敵は何体も出現します。なのでEnemyオブジェクトをプレハブ化して使い回します。
ヒエラルキーの「Enemy」オブジェクトをプロジェクトの「Assets/Prefabs」フォルダにドラッグ&ドロップして、プレハブ化します。するとEnemyの「Enemy Controller > Path Data」変数のアサインが外れてしまいました。これはプレハブ化したオブジェクトはヒエラルキーのオブジェクトを参照できないためです。
この問題を解決するために、「Path」オブジェクトもプレハブ化します。そして、プレハブの「Enemy」にプレハブの「Path」をアサインして一件落着です。
さいごに
今回は敵の移動方向に応じてアニメーションを変更する処理を実装しました。
このペースでやってたら、いつゲームが完成するのだろうとちょっと不安になっております。
でわでわ
コメント