【Unity】タワーディフェンス(10) 複数の砲台から選択して配置【クソゲー制作】

タワーディフェンスを作る(10)

Unity初心者が2Dタワーディフェンスを作るシリーズ第10回目です。まだまだ終わりそうにありません。

今回は複数の砲台から1つを選択して配置する処理を実装していきます。

環境
  • Mac mini (M1, 2020)
  • Unity 2022.3.36f1
目次

砲台のデータをScriptableObjectで管理する

複数の砲台のデータはScriptableObjectで管理します。敵データの管理でも使いましたね。復習です。

TurretSettingスクリプトの作成

「Assets/Scripts」の下に新しいC#スクリプトを作成して、名前を「TurretSetting」にします。

内容は次のとおりです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System; // [Serializable]を使うために必要な宣言

[CreateAssetMenu(fileName = "TurretSetting", menuName = "ScriptableObject/Turret Setting")]
public class TurretSetting : ScriptableObject
{
    public List<TurretData> turretDataList = new List<TurretData>();

    [Serializable]
    public class TurretData
    {
        public string id;   // ID
        public string name; // 名前
        public int attackPower; // 攻撃力
        public float attackInterval; // 攻撃間隔
        public float attackRange; // 攻撃範囲
        public Sprite turretHeadSprite; // 砲身の画像
    }
}

[CreateAssetMenu]属性でUnityのメニューにScriptableObjectを作成するための項目を追加します。

public class TurretSetting : ScriptableObjectでクラスを宣言します。

turretDataListは砲台のデータを格納するリストです。

TurretDataクラスは個々の砲台のデータを保持するクラスです。

ScriptableObjectのアセットを作成

STEP

「Assets/Data」フォルダで右クリックして「Create > ScriptableObject > Turret Setting」を選択してScriptableObjectを作成します。

タワーディフェンス111
STEP

「Assets/Data」フォルダの下に「TurretSetting」というアセットが作成されました。

タワーディフェンス112

砲台データの登録

STEP

「Assets/Data/TurretSetting」を選択して、インスペクターで「Turret Data List」を開くと「List is empty」となっています。

タワーディフェンス113
STEP

「+」をクリックして砲台の情報を追加していきます。

タワーディフェンス114

DBManagerスクリプトの修正

STEP

DBManagerスクリプトを修正します。DBManagerはScriptabeObjectを一元管理するためのシングルトンクラスでしたね。このクラスでturretSettingを扱えるようにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DBManager : MonoBehaviour
{
    public static DBManager instance;
    public EnemySetting enemySetting;
    // ここから 追加
    public TurretSetting turretSetting;
    // ここまで

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}
STEP

DBManagerオブジェクトのインスペクターで「DB Manager (Script) > Turret Setting」「Assets/Data/TurretSetting」をドラッグ&ドロップしてアサインします。

タワーディフェンス115

砲台選択用のUIの作成

現在はマップのタイルをクリックすると即座に砲台が設置されますが、これをサイドバーの砲台アイコンから選択してマップ上に設置するようにします。

完成図は次のようになります。

タワーディフェンス125

SideBarオブジェクトの作成

ゲーム画面の右側にUIを表示するための領域を作ります。

STEP

ヒエラルキーのCanvasオブジェクト上で右クリック「UI > Panel」を選択します。Canvasの配下にPanelオブジェクトが作成されます。名前を「SideBar」にします。

タワーディフェンス116
STEP

SideBarがゲーム画面の右側に配置されるようにRect Transformを調整します。

タワーディフェンス117
STEP

「Image > Color」を透明にします。

タワーディフェンス118

TurretOptionsオブジェクトの作成

SideBarオブジェクトの配下に砲台を選択するためのアイコンを表示する領域を作ります。

STEP

ヒエラルキーのSideBarオブジェクト上で右クリック「UI > Panel」を選択します。SideBarの配下にPanelオブジェクトが作成されるので、名前を「TurretOptions」にします。

STEP

TurretOptionsがサイドバーのの上部に配置されるようにRect Transformを調整します。

タワーディフェンス119
STEP

「Image > Color」を透明にします。

STEP

「Add Component」ボタンをクリックして、「Grid Layout Group」コンポーネントを追加します。これは子オブジェクトを等間隔で配置するためのコンポーネントです。砲台選択用のボタンを整列させるために使います。

タワーディフェンス120

砲台選択用ボタンの作成

STEP

TurretOptionsオブジェクト上で右クリック「UI > Button – TextMeshPro」を選択します。TurretOptionsの配下にButtonオブジェクトが作成されます。

タワーディフェンス121

「TMP Importer」ウインドウが表示されたら「Import TMP Essentials」ボタンをクリックします。ExamplesはインポートしなくてOKです。

オブジェクトの名前を「TurretBtn0」にします。

STEP

TurretBtn0の配下にあるText(TMP)オブジェクトを削除します。

今回、ボタンには砲台のアイコンのみを表示し、文字は表示しません。

タワーディフェンス122
STEP

TurretBtn0オブジェクト上で右クリック「UI > Image」を選択します。TurretBtn0の配下にImageオブジェクトが作成されます。名前を「TurretBaseImage」にします。

TurretBaseImageオブジェクトのインスペクターの「Image > Source Image」「Assets > Images > Turret > turret_base1」をドラッグ&ドロップしてアサインします。これは砲台の台座の画像です。

STEP

もう一度、TurretBtn0オブジェクト上で右クリック「UI > Image」を選択します。TurretBtn0の配下にImageオブジェクトが作成されます。名前を「TurretHeadImage」にします。

TurretHeadImageオブジェクトのインスペクターの「Image > Source Image」「Assets > Images > Turret > turret4」をドラッグ&ドロップしてアサインします。これは砲台の砲身の画像です。

STEP

TurretBtn0オブジェクトのインスペクターでボタンの色を設定します。

タワーディフェンス123
Normal Color通常時の色#444444
Highlighted Colorマウスオーバー時の色#999999
Pressed Color押下時の色#CCCCCC
Selected Color選択時の色#999999
Disabled Color無効時の色#C8C8C8

上記の手順を繰り返して複数の砲台選択用ボタンを作成します。

Grid Layout Groupの設定

TurretOptionsオブジェクトのインスペクターでGrid Layout Groupの設定をします。これを適切に設定すると砲台アイコンがきれいに並びます。

タワーディフェンス124
Paddingグリッド全体の外側の余白すべて0
Cell Size各グリッドセルの幅と高さX: 100, Y: 100
Spacing各セル間の間隔(横・縦)X: 20, Y: 20
Start Cornerグリッドの配置開始位置Upper Left (左上から開始)
Start Axis配置の優先方向Horizontal (横方向に並べる)
Child Alignment子要素のグリッド内での整列方法Middle Center (中央に整列)
Constraintグリッドの行・列数の制限Flexible (自動調整)

選択した砲台を配置する

TurretGeneratorスクリプトの修正

TurretGeneratorスクリプトを修正します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps; // Tilemapを扱うために必要な宣言

public class TurretGenerator : MonoBehaviour
{
    [SerializeField]
    private GameObject turretPrefab; // 砲台のプレハブ
    [SerializeField]
    private Grid grid; // Grid_BaseのGrid、Tilemapの座標を取得するため
    [SerializeField]
    private Tilemap tilemaps; // Grid_WayのTilemap
    private Vector3Int gridPos;  // Tilemapのセル座標
    private HashSet<Vector3Int> occupiedCells = new HashSet<Vector3Int>(); // 砲台配置済みセルを代入
    // ここから 変数追加
    private TurretSetting.TurretData selectedTurretData = null; // 選択された砲台のデータ
    // ここまで

    void Update()
    {
        // ...省略...
    }

    /// <summary>
    /// 砲台生成
    /// </summary>
    /// <param name="gridPos"></param>
    private void GenerateTurret(Vector3Int gridPos)
    {
        // 砲台が選択されていなければ何もしない
        if (selectedTurretData == null)
        {
            Debug.Log("砲台が選択されていません");
            return;
        }
        // 配置済みの場合は処理を中断
        if (occupiedCells.Contains(gridPos))
        {
            Debug.Log("このセルにはすでに砲台が配置されています");
            return;
        }
        // クリックした位置に砲台を配置
        GameObject turret = Instantiate(turretPrefab, gridPos, Quaternion.identity);
        // 砲台の位置がタイルの左下を 0,0 として生成しているので、タイルの中央にくるように位置を調整
        turret.transform.position = new Vector2(turret.transform.position.x + 0.5f, turret.transform.position.y + 0.5f);
        // ここから 追加
        // TurretControllerを取得する
        TurretController turretController = turret.GetComponent<TurretController>();
        // 砲台データの初期化
        turretController.InitializeTurret(selectedTurretData);
        // ここまで
        // 配置されたセルを登録
        occupiedCells.Add(gridPos);
        // 砲台を設置したら選択をリセット
        selectedTurretData = null;
    }

    // ここから メソッド追加
    /// <summary>
    /// 砲台を選択する
    /// </summary>
    public void SelectTurret(int index)
    {
        selectedTurretData = DBManager.instance.turretSetting.turretDataList[index];
        Debug.Log($"{selectedTurretData.name} を選択");
    }
    // ここまで
}

selectedTurretData変数を追加しました。この変数に選択された砲台のデータを保持します。

GenerateTurret()メソッド内でturretController.InitializeTurretを呼び出してselectedTurretDataで砲台を初期化します。

SelectTurret()メソッドはselectedTurretDataに砲台のデータを代入します。あとで砲台選択用ボタンのOnClick()から呼び出します。

TurretControllerスクリプトの修正

STEP

TurretControllerスクリプトを修正します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurretController : MonoBehaviour
{
    [SerializeField]
    private int attackPower = 1; // 攻撃力
    [SerializeField]
    private float attackInterval = 60.0f; // 攻撃間隔(単位はフレーム)
    [SerializeField]
    private Transform turretHead; // 砲身のTransform
    [SerializeField]
    private GameObject shellPrefab; // 砲弾のプレハブ
    [SerializeField]
    private Transform firePoint; // 砲弾の発射位置
    // ここから 変数追加
    [SerializeField]
    private CircleCollider2D attackRange; //攻撃範囲のコライダー
    [SerializeField]
    private SpriteRenderer turretHeadSpriteRenderer; // 砲身のSpriteRenderer
    //ここまで
    private List<EnemyController> enemiesInRange = new List<EnemyController>(); // 攻撃範囲内の敵リスト
    private EnemyController targetEnemy = null; // 現在のターゲット
    private bool isAttacking = false; // 攻撃中フラグ
    private Coroutine attackCoroutine; // 現在の攻撃コルーチン


    private void Update()
    {
        // ...省略...
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        // ...省略...
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        // ...省略...
    }

    // ここから メソッド追加
    /// <summary>
    /// 砲台データを初期化
    /// </summary>
    public void InitializeTurret(TurretSetting.TurretData turretData)
    {
        attackPower = turretData.attackPower; // 攻撃力を設定
        attackInterval = turretData.attackInterval; // 攻撃間隔を設定
        attackRange.radius = turretData.attackRange; // 攻撃範囲を設定
        turretHeadSpriteRenderer.sprite = turretData.turretHeadSprite; // 砲身の画像を設定
        Debug.Log($"生成された砲台: {turretData.name}");
    }
    //ここまで

    /// <summary>
    /// 砲台に最も近い敵を選択
    /// </summary>
    private void UpdateTargetEnemy()
    {
        // ...省略...
    }

    /// <summary>
    /// 攻撃間隔管理
    /// </summary>
    public IEnumerator ManageAttacks()
    {
        // ...省略...
    }

    /// <summary>
    /// 攻撃
    /// </summary>
    private void Attack()
    {
        // ...省略...
    }

    /// <summary>
    /// 砲身を敵の方向に回転させる
    /// </summary>
    private void RotateTurretHeadTowardsEnemy()
    {
        // ...省略...
    }
}

2つの変数attackRangeturretHeadSpriteRendererを追加しました。

そして、砲台データを初期化するInitializeTurret()メソッドを追加しました。

STEP

「Assets > Prefabs > Turret」をダブルクリックしてプレハブの編集モードへ入ります。

インスペクターの「Turret Controller (Script) > Attack Range」AttackRangeオブジェクトをドラッグ&ドロップしてアタッチします。

タワーディフェンス126
STEP

「Turret Controller (Script) > Turret Head Sprite Renderer」TurretHeadオブジェクトをドラッグ&ドロップしてアタッチします。

タワーディフェンス127

OnClickの設定

ヒエラルキーの「TurretBtn0」を選択して、インスペクターの「Button > On Click ()」を次のように設定します。

タワーディフェンス128

他のボタンも同じように設定します。IDの値(右下の入力項目)はボタンの番号に合わせて0, 1, 2, 3のように設定します。

動作確認

動作確認をしてみましょう。

選択した砲台が設置されるようになりました。いえい。

さいごに

いろんな種類の砲台を設置できるようになりました。ここまでで全工程の半分が完成した感じかな?(希望的観測)

でわでわ

タワーディフェンスを作る(10)

この記事が気に入ったら
いいね または フォローしてね!

シェアしてね

コメント

コメントする

目次