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のアセットを作成
「Assets/Data」フォルダで右クリックして「Create > ScriptableObject > Turret Setting」を選択してScriptableObjectを作成します。

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

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

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

DBManagerスクリプトの修正
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);
}
}
}
DBManagerオブジェクトのインスペクターで「DB Manager (Script) > Turret Setting」に「Assets/Data/TurretSetting」をドラッグ&ドロップしてアサインします。

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

SideBarオブジェクトの作成
ゲーム画面の右側にUIを表示するための領域を作ります。
ヒエラルキーのCanvasオブジェクト上で右クリック「UI > Panel」を選択します。Canvasの配下にPanelオブジェクトが作成されます。名前を「SideBar」にします。

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

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

TurretOptionsオブジェクトの作成
SideBarオブジェクトの配下に砲台を選択するためのアイコンを表示する領域を作ります。
ヒエラルキーのSideBarオブジェクト上で右クリック「UI > Panel」を選択します。SideBarの配下にPanelオブジェクトが作成されるので、名前を「TurretOptions」にします。
TurretOptionsがサイドバーのの上部に配置されるようにRect Transformを調整します。

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

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

「TMP Importer」ウインドウが表示されたら「Import TMP Essentials」ボタンをクリックします。ExamplesはインポートしなくてOKです。
オブジェクトの名前を「TurretBtn0」にします。
TurretBtn0の配下にあるText(TMP)オブジェクトを削除します。
今回、ボタンには砲台のアイコンのみを表示し、文字は表示しません。

TurretBtn0オブジェクト上で右クリック「UI > Image」を選択します。TurretBtn0の配下にImageオブジェクトが作成されます。名前を「TurretBaseImage」にします。
TurretBaseImageオブジェクトのインスペクターの「Image > Source Image」に「Assets > Images > Turret > turret_base1」をドラッグ&ドロップしてアサインします。これは砲台の台座の画像です。
もう一度、TurretBtn0オブジェクト上で右クリック「UI > Image」を選択します。TurretBtn0の配下にImageオブジェクトが作成されます。名前を「TurretHeadImage」にします。
TurretHeadImageオブジェクトのインスペクターの「Image > Source Image」に「Assets > Images > Turret > turret4」をドラッグ&ドロップしてアサインします。これは砲台の砲身の画像です。
TurretBtn0オブジェクトのインスペクターでボタンの色を設定します。

Normal Color | 通常時の色 | #444444 |
---|---|---|
Highlighted Color | マウスオーバー時の色 | #999999 |
Pressed Color | 押下時の色 | #CCCCCC |
Selected Color | 選択時の色 | #999999 |
Disabled Color | 無効時の色 | #C8C8C8 |
上記の手順を繰り返して複数の砲台選択用ボタンを作成します。
Grid Layout Groupの設定
TurretOptionsオブジェクトのインスペクターでGrid Layout Groupの設定をします。これを適切に設定すると砲台アイコンがきれいに並びます。

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スクリプトの修正
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つの変数attackRange
とturretHeadSpriteRenderer
を追加しました。
そして、砲台データを初期化するInitializeTurret()
メソッドを追加しました。
「Assets > Prefabs > Turret」をダブルクリックしてプレハブの編集モードへ入ります。
インスペクターの「Turret Controller (Script) > Attack Range」にAttackRangeオブジェクトをドラッグ&ドロップしてアタッチします。

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

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

他のボタンも同じように設定します。IDの値(右下の入力項目)はボタンの番号に合わせて0
, 1
, 2
, 3
のように設定します。
動作確認
動作確認をしてみましょう。
選択した砲台が設置されるようになりました。いえい。
さいごに
いろんな種類の砲台を設置できるようになりました。ここまでで全工程の半分が完成した感じかな?(希望的観測)
でわでわ
コメント