Unity初心者が2Dタワーディフェンスを作っています。今回は敵を作成して、移動経路に沿って移動させる処理を実装してみます。
- Mac mini (M1, 2020)
- Unity 2022.3.36f1
画像のインポート
「Assets/Images」フォルダの下に「Enemy」フォルダを作成して、そこに敵の画像をドラッグ&ドロップしてインポートします。
画像はKenneyさんからお借りして私が手を加えたものです。上下左右方向に移動するスライムのキャラチップです。
インポートした画像の「Pixels Per Unit」の値を64に変更します。
画像をまとめて選択して値を変更して「Apply」ボタンです。
「Pixels Per Unit」の値が大きければ敵は小さく表示され、値が小さければ大きく表示されます。お好みで調整してください。
オブジェクトの作成とアニメーション
右方向へ移動する敵のオブジェクトを作成します。
「Assets/Images/Enemy」にあるslime_rightとslime_walk_rightをまとめて選択してヒエラルキービューにドラッグ&ドロップします。
複数の画像をまとめてヒエラルキーにドラッグ&ドロップすると、アニメーションを作ることができます。
「Create New Animation」ウインドウが開くので「Assets/Animations」フォルダを作成し、名前を「Enemy0_right」にしてアニメーションクリップを保存します。
ヒエラルキーにslime_rightという名前のオブジェクトが作成されるので名前をEnemyに変更します。
このオブジェクトのインスペクターで「Sprite Renderer > Additional Settings > Sorting Layer」を「Object」に変更します。これで敵が最前面に表示されるようになります。
この時点でゲームを実行するとGameビューでスライムが歩くアニメーションを確認することができます。めっちゃ速歩きです。アニメーションの設定を調整しましょう。
メニューバーの「Window > Animation > Animation」を選択して、Animationビューを開きます。
Animationビューの右上の三点リーダーをクリックして「Frames」を選択、「Samples」の値を変更するとアニメの速度が変わります。2くらいがちょうどいいのではないでしょうか。お好みでどうぞ。
Animationビューの再生ボタンを押して動作を確認してください。
いい感じ〜。
敵の移動経路の作成
マップの道の上に複数のゲームオブジェクトを並べて、それらを繋いで敵の移動経路を作成します。
経路用オブジェクトの作成
ヒエラルキービューで右クリック「Create Empty」を選択して空オブジェクトを作成します。名前を「Path」に変更します。
これは複数のオブジェクトを格納するフォルダの役割を果たします。
「Path」の上で右クリック「Create Empty」を選択してPathの配下に空オブジェクトを作成します。名前を「PositionStart」に変更します。
これが敵の移動経路のスタート地点をあらわすオブジェクトです。
「PositionStart」は形のないオブジェクトです。SceneビューやGameビューに表示されません。それでは困るので表示されるようにします。
インスペクターで左上の立方体をクリックします。「Select Icon」が表示されるのでお好みのアイコンを選択します。
これで「PositionStart」オブジェクトがSceneビューに表示されるようになります。
「PositionStart」を敵を出現させる位置に移動します。Game画面に映らない位置にすると、画面外から敵が入ってくるように見えます。
「Path」の上で右クリック「Create Empty」を選択してPathの配下に空オブジェクトを作成します。名前を「PositionGoal」に変更します。移動経路のゴール地点ですね。
アイコンを設定して、マップ上の防衛拠点建設予定地に移動します。ここにはあとでお城を配置します。
PositionStartとPositionGoalを結ぶ経路の曲がり角に空オブジェクトを作成します。名前は「Position01」「Position02」…としました。
アイコンを設定して、各曲がり角に配置します。
PathDataスクリプトの作成
上記で作成したオブジェクトたちを繋いで経路を作成するためのスクリプトを書きます。
「Assets/Scripts」フォルダの下で右クリック「Create > C# Script」を選択して、C#スクリプトを作成します。名前は「PathData」にしました。
PathData.csの内容は次のとおりです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathData : MonoBehaviour
{
public Transform positionStart; // スタート地点
public Transform[] pathArray; // 敵の移動経路の配列
}
ヒエラルキーの「Path」オブジェクトにPathData.csをドラッグ&ドロップしてアタッチします。
「Path」オブジェクトのインスペクターで「Path Data (Script) > Position Start」に「PositionStart」オブジェクトをドラッグ&ドロップしてアサインします。
「Path Array」にスタートからゴールまでの経路用オブジェクトを順番にアサインします。
敵の移動処理
マップ上の移動経路に沿って敵を移動させるアニメーションは、DOTweenのDOPathメソッドを使って実装します。
DOTweenのインポート
- UnityアセットストアでDOTween (HOTween v2)を開いて「マイアセットに追加する」ボタンをクリックします。
- マイアセットの「Unityで開く」ボタンをクリックします。
- Unityの「Package Manager」ウインドウが開くので、右上の「Download」ボタンをクリックします。
- ダウンロードが完了したら「Import」ボタンをクリックします。
- 「Import Unity Package」ウインドウが開いたら「Import」ボタンをクリックします。
- 「New Version of DOTween Imported」ウインドウが開いたら「Open DOTween Utility Panel」ボタンをクリックします。
- 「DOTween Utility Panel」ウインドウが開いたら「Setup DOTween…」ボタンをクリックします。
- 次の画面で「Apply」ボタンをクリックします。
EnemyControllerスクリプトの作成
「Assets > Scripts」フォルダに新規C#スクリプトを作成します。名前を「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から取得した座標を格納するための配列
void Start()
{
// 経路を取得
path = pathData.pathArray.Select(x => x.position).ToArray();
// 経路に沿って移動
transform.DOPath(path, 1000 / speed).SetEase(Ease.Linear);
}
}
18行目ではLINQのSelect()
メソッドを使って、pathData.pathArray
内の各Transformオブジェクトのposition
プロパティを抽出しています。それをToArray()
で配列に変換してpath
に格納しています。
20行目、DOPath()
メソッドを使って、移動経路に沿って敵を移動させています。第1引数は移動経路の座標をあらわすVector3の配列です。第2引数は経路全体を移動するのにかかる時間(秒)です。1000 / speed
とすることで、speed
の値が小さければゆっくり、大きければ早く移動します。
EnemyオブジェクトにEnemyController.csをドラッグ&ドロップしてアタッチします。
Enemyオブジェクトのインスペクターで「Enemy Controller > Path Data」にpathオブジェクトをドラッグ&ドロップしてアサインします。「Speed」は30にします。
ゲームを実行すると敵が経路に沿って移動しました。わーい。
経路の長さにかかわらず速度を一定にする
上記のEnemyController.csでは、同じspeed
を設定しても移動経路の長さによって敵の移動速度が変わってしまいます。なぜならDOPath()
の第2引数は経路全体を移動するのにかかる時間をあらわすからですね。経路が短ければゆっくり、長ければ速く移動してしまうわけです。
この問題を解消するためにEnemyController.csを修正します。
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から取得した座標を格納するための配列
void Start()
{
// 経路を取得
path = pathData.pathArray.Select(x => x.position).ToArray();
// 経路の総距離を計算
float totalDistance = CalculatePathLength(path);
// 移動時間を計算 (距離 ÷ 速度)
float moveDuration = totalDistance / speed;
// 経路に沿って移動
transform.DOPath(path, moveDuration).SetEase(Ease.Linear);
}
/// <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;
}
}
21行目、CalculatePathLength()
で経路の総距離を計算します。
23行目、距離÷速度で移動時間を計算します。はじきの公式ですね。
25行目、DOPath()
の第2引数に1000 / speed
の代わりにmoveDuration
を入れます。
Unityでゲームを実行してみます。ものすごい速度で敵が駆け抜けました。これは移動時間の計算方法が変わったからです。speed
の値を調整しましょう。1〜2くらいがちょうどいいのではないでしょうか。
さいごに
今回はきゃわゆいスライムちゃんがマップ上を移動する処理を実装しました。
次回は移動方向に応じてアニメーションを変更してみたいと思います。
でわでわ
コメント