Unity ゲーム開発 生成AI

Unity素人がAI活用で進める独学個人ゲーム開発 #9_アイテムの実装②

本記事では、ChatGPTを活用してUnityの自機の強化に使うアイテムを実装する過程を紹介します。

記事の目的

前回、アイテムが生成される仕組みの実装を行いました。
前回記事 ⇒ #8_アイテムの実装①


コインのようなものを破壊すると銃弾の攻撃力が上がるようになっています。

しかし、枠組みができたたけでまだまだゲームとして楽しめるものにはなっていません。

  • 攻撃力の強化だけしかないので自機増加や連射速度UPなどの種類を増やす。
  • アイテムの残り体力を表示する。
  • ゲーム進行につれて火力が上がるはずなのでその分アイテムの体力も上がるようにする。
  • アイテムを破壊したときに発生する効果を明示する。

UIなど文字などの表記が必要なものは次回以降行うので今回はアイテム種類を増やしていきたいと思います。

アイテム種類の増加

アイテムタイプのランダム化

実際のところItem.csにItemTypeという項目はすでに存在しています。

しかし、1種類選択してそれが出てくるだけなのでこれを改善していきます。

現在、ItemTypeが3種類ありますが1種類しか出ないようになっています。
これをランダムに生成されるようにしたいです。

ChatGPTの回答(クリックで展開)
とても良い改善ポイントです!
以下でやり方をしっかり整理して説明します。

🌟 現状の推測
今の ItemSpawner スクリプトでは:

Instantiate(itemPrefab, spawnPoint.position, Quaternion.identity);

このとき itemPrefab は単一のPrefab(例: PowerUp用Prefab)なので、
生成されるのは必ず同じ ItemType です。

やりたいこと
生成するたびに:
ItemType(PowerUp / RapidFireUp / AddClone)をランダムに割り当てる。

🛠 改善案1:1種類のPrefabにランダム割り当て
Prefabは1種類のまま、生成後に itemType をランダム設定する方式です。

void SpawnItems()
{
    int[] indices = { 0, 1, 2, 3, 4 };

    for (int i = indices.Length - 1; i > 0; i--)
    {
        int rand = Random.Range(0, i + 1);
        int temp = indices[i];
        indices[i] = indices[rand];
        indices[rand] = temp;
    }

    for (int i = 0; i < 3; i++)
    {
        Transform spawnPoint = spawnPoints[indices[i]];
        GameObject itemObj = Instantiate(itemPrefab, spawnPoint.position, Quaternion.identity);

        Item itemScript = itemObj.GetComponent<Item>();
        if (itemScript != null)
        {
            int typeCount = System.Enum.GetValues(typeof(Item.ItemType)).Length;
            itemScript.itemType = (Item.ItemType)Random.Range(0, typeCount);
        }
    }
}

💥 ポイント
System.Enum.GetValues(typeof(Item.ItemType)).Length → Enumの要素数(=3)を取得
Random.Range(0, typeCount) → 0~2のランダムなintを選び、Enumにキャスト

改善案2:Prefabを3種類用意して選ぶ
もし見た目や挙動がPrefabごとに完全に異なるなら、
ItemPrefabを3種類用意して、Spawner側で選ぶ方法もあります。

public GameObject[] itemPrefabs;

...

GameObject prefabToSpawn = itemPrefabs[Random.Range(0, itemPrefabs.Length)];
Instantiate(prefabToSpawn, spawnPoint.position, Quaternion.identity);

🚀 おすすめ(まずは案1)
今のコードなら Prefabは1種類で、itemTypeをランダム設定する 案1がシンプルで実装しやすいです。

改善案1についてコードの内容が理解できないため、改めて聞いてみます。

コードについてC#とUnityの実装の観点から詳しく解説してください。

ChatGPTの回答(クリックで展開)
とても良い質問です!
以下に C# と Unity の両方の観点から、この SpawnItems() メソッドを詳しく解説します。

🌟 コード全文

void SpawnItems()
{
    int[] indices = { 0, 1, 2, 3, 4 };

    // シャッフル
    for (int i = indices.Length - 1; i > 0; i--)
    {
        int rand = Random.Range(0, i + 1);
        int temp = indices[i];
        indices[i] = indices[rand];
        indices[rand] = temp;
    }

    // ランダムな3か所に生成
    for (int i = 0; i < 3; i++)
    {
        Transform spawnPoint = spawnPoints[indices[i]];
        GameObject itemObj = Instantiate(itemPrefab, spawnPoint.position, Quaternion.identity);

        // ランダムなItemTypeを割り当て
        Item itemScript = itemObj.GetComponent<Item>();
        if (itemScript != null)
        {
            int typeCount = System.Enum.GetValues(typeof(Item.ItemType)).Length;
            itemScript.itemType = (Item.ItemType)Random.Range(0, typeCount);
        }
    }
}

🔍 詳細解説

✅ 1️⃣ int[] indices = { 0, 1, 2, 3, 4 };
C#観点: 配列 indices を 0~4 で初期化。これが5か所の spawnPoint のインデックスを表す。
Unity観点: spawnPoints は Transform[] で、各スポーン地点の位置情報を持っている。

✅ 2️⃣ シャッフル処理(Fisher-Yatesシャッフル)

for (int i = indices.Length - 1; i > 0; i--)
{
    int rand = Random.Range(0, i + 1);
    int temp = indices[i];
    indices[i] = indices[rand];
    indices[rand] = temp;
}

C#観点: 配列をランダムに並べ替える標準アルゴリズム。
Unity観点: Random.Range(0, i + 1) は上限を含む。毎回 spawnPoints の順序がシャッフルされる。

✅ 3️⃣ ランダムな3か所に生成

for (int i = 0; i < 3; i++)
{
    Transform spawnPoint = spawnPoints[indices[i]];
    GameObject itemObj = Instantiate(itemPrefab, spawnPoint.position, Quaternion.identity);
}

C#観点: シャッフル済み indices から最初の3つを取り出し、spawnPoints 配列から Transform を取得。
Unity観点: Instantiate() で itemPrefab を指定位置に生成、回転はデフォルト(Quaternion.identity)。

✅ 4️⃣ ItemType をランダム割り当て

Item itemScript = itemObj.GetComponent<Item>();
if (itemScript != null)
{
    int typeCount = System.Enum.GetValues(typeof(Item.ItemType)).Length;
    itemScript.itemType = (Item.ItemType)Random.Range(0, typeCount);
}

C#観点: Enumの要素数を取得し、int → Enumキャスト。
Unity観点: Inspector 上では itemType が Enum として表示。Prefabは1つでも種類をランダム化可能。

🛠 実装のポイント(良い点)
✅ シャッフルで偏りのないランダム選択。
✅ Prefab1つで多様な種類を表現(Prefab増殖を防ぐ)。
✅ Enumの要素数を自動取得 → 将来ItemTypeが増えてもコード修正不要。

自分の理解でまとめていきます

C#メモ
コード / 変数 意味
indice indexの複数形
Random.Range(0, i + 1) 0 から i の範囲でランダムな整数を作る
Fisher-Yatesシャッフル 配列の要素を完全ランダムに並べ替えるアルゴリズム
System.Enum.GetValues() 指定した enum の値一覧を 配列 で返すメソッド

Fisher-Yatesシャッフルというのは聞いたことすらありませんでした。
これによりシャッフルされた配列から最初の3つを抜き出して生成位置を決定。

int typeCount = System.Enum.GetValues(typeof(Item.ItemType)).Length;
C#観点の詳しい解説をお願いします。

ChatGPTの回答(クリックで展開)
C#観点 解説

Item.ItemTypeenum(列挙型)

System.Enum.GetValues() は、指定した enum の値一覧を 配列 で返すメソッドです。
そのため、.Length を使うことで要素数(種類数)を取得できます。

🌟

enum ItemType { PowerUp, RapidFireUp, AddClone }

この場合、

System.Enum.GetValues(typeof(ItemType))

は次のような配列を返します:
[PowerUp, RapidFireUp, AddClone]

そのため、

.Length

を呼ぶと要素数は 3 になります。

活用のポイント
・ItemType が将来増えても、自動的にカウントされるのでコード修正不要。
・ランダム選択に便利(例:Random.Range(0, typeCount))。

生成位置に対してアイテムタイプの配列番号を対応させたものを返す?
コードの働きが初心者には少し難しく感じます。

理解できている自信はありませんが、困るのは未来の自分にまかせて次に進みましょう。

自機の増加

銃弾の火力UP、連射速度UPは過去のスクリプトで実装していたため、そのアイテムが現れて破壊できれば効果が発動します。
(過去の記事はこちら⇒アイテムの実装①

しかし、自機の増加については何も実装されていないため、ここから実装していきます。

また、ここから少しだけオリジナリティーを出していこうかと思います。
料理下手な人がやるアレンジみたいなことにならないよう慎重に進めていきます。

設計案出し

自機を増加させる仕組みを作っていきます。
以下の条件で自機が増減したときの仕組みを作成してください。

・自機は最大5機まで増える
・最初の自機に対して右→左→右後ろ→左後ろで増加
・どの自機が破壊されたかに関わらず、1機破壊されたときは1つ前の隊列に戻る
・ボタンの入力で隊列を縦1直線、横1直線に変化させることができる。また、元の隊列に戻すこともできる。

ChatGPTの回答(クリックで展開)
とても良い要件です!ゲーム性がぐっと面白くなる要素なので、以下に整理して設計案・実装案を順番に解説します。

🌟 要件まとめ
✅ 自機は最大5機。
✅ 増える順番:主機 → 右 → 左 → 右後ろ → 左後ろ(主機から見ての相対座標)。
✅ 減るときはどの分身が壊れても1つ前の隊列に戻る。
✅ フォーメーションをボタン操作で切り替え:元の隊列、縦一列、横一列。

🏗 設計案
1️⃣ 自機と分身の管理方法
- PlayerController が「主機」。
- 分身はプレハブ(ClonePrefab)を生成し、List<GameObject> clones で管理。

2️⃣ 座標パターンの定義
元の隊列:右 (1,0)、左 (-1,0)、右後ろ (0.5,-1)、左後ろ (-0.5,-1)
縦一列:(0,0)、(0,-1)、(0,-2)、(0,-3)、(0,-4)
横一列:(0,0)、(1,0)、(2,0)、(3,0)、(4,0)

3️⃣ 隊列変更
- Qキー:元の隊列
- Wキー:縦一列
- Eキー:横一列

4️⃣ 破壊処理
分身に CloneController をつけ、破壊時に PlayerController の RemoveClone() を呼ぶ。

主要コード例

// PlayerController.cs
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public GameObject clonePrefab;
    private List<GameObject> clones = new List<GameObject>();
    private int maxUnits = 5;
    private string formation = "original";

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Q)) { formation = "original"; UpdateFormation(); }
        if (Input.GetKeyDown(KeyCode.W)) { formation = "vertical"; UpdateFormation(); }
        if (Input.GetKeyDown(KeyCode.E)) { formation = "horizontal"; UpdateFormation(); }
    }

    public void AddClone()
    {
        if (clones.Count + 1 >= maxUnits) return;
        GameObject newClone = Instantiate(clonePrefab, transform.position, Quaternion.identity);
        newClone.GetComponent<CloneController>().SetPlayer(this);
        clones.Add(newClone);
        UpdateFormation();
    }

    public void RemoveClone()
    {
        if (clones.Count > 0)
        {
            GameObject lastClone = clones[clones.Count - 1];
            clones.RemoveAt(clones.Count - 1);
            Destroy(lastClone);
            UpdateFormation();
        }
    }

    void UpdateFormation()
    {
        List<Vector2> positions = GetFormationPattern(clones.Count + 1);
        transform.localPosition = positions[0];

        for (int i = 0; i < clones.Count; i++)
        {
            clones[i].transform.localPosition = transform.position + (Vector3)positions[i + 1];
        }
    }

    List<Vector2> GetFormationPattern(int unitCount)
    {
        List<Vector2> positions = new List<Vector2>();

        if (formation == "original")
        {
            positions.Add(Vector2.zero);
            if (unitCount >= 2) positions.Add(new Vector2(1, 0));
            if (unitCount >= 3) positions.Add(new Vector2(-1, 0));
            if (unitCount >= 4) positions.Add(new Vector2(0.5f, -1));
            if (unitCount >= 5) positions.Add(new Vector2(-0.5f, -1));
        }
        else if (formation == "vertical")
        {
            for (int i = 0; i < unitCount; i++)
                positions.Add(new Vector2(0, -i));
        }
        else if (formation == "horizontal")
        {
            for (int i = 0; i < unitCount; i++)
                positions.Add(new Vector2(i, 0));
        }

        return positions;
    }
}

// CloneController.cs
using UnityEngine;

public class CloneController : MonoBehaviour
{
    private PlayerController player;

    public void SetPlayer(PlayerController controller)
    {
        player = controller;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Enemy"))
        {
            player.RemoveClone();
            Destroy(gameObject);
        }
    }
}

注意ポイント
✅ 分身Prefabには Collider2D と Rigidbody2D(Kinematic)をつける。
✅ Tag管理(Player, Clone, Enemy)を設定する。
✅ 主機が破壊されたときはゲームオーバー処理を分ける。

基本的なところは抑えてくれているような気がしますが、
Unityで手を動かす前に気になるところを確認していきます。

設計案確認

主機がやられたときにゲームオーバーになってしまう設計でしょうか?
主機と分身に区別は不要なのですが。

ChatGPTの回答(クリックで展開)
とても重要な質問です!整理して説明します。

🌟 現在の設計の確認
現状では:
✅ 主機(PlayerController 付き)
✅ 分身(CloneController 付き)
と区別され、主機が破壊されるとゲームオーバーになる設計です。

🛠 あなたの希望(要件修正)
✅ 主機と分身を区別しない。
✅ どの機体が破壊されても総機数を1減らし、
 最後の1機がやられたときだけゲームオーバーにする。

💥 改修方針
1️⃣ PlayerController を全体管理者にする(隊列全体の管理者)。
2️⃣ 全員が同じPrefab(例: UnitPrefab)を使う。
3️⃣ 各機体が被弾時、PlayerController に報告する。
4️⃣ 残り0機ならゲームオーバー。

実装例

// UnitController.cs(全機共通)
using UnityEngine;

public class UnitController : MonoBehaviour
{
    public PlayerController playerController;

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Enemy"))
        {
            playerController.UnitDestroyed(this);
            Destroy(gameObject);
        }
    }
}

// PlayerController.cs(抜粋)
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public List<UnitController> units = new List<UnitController>();
    public GameObject unitPrefab;

    void Start()
    {
        var mainUnit = Instantiate(unitPrefab, transform.position, Quaternion.identity);
        var unitCtrl = mainUnit.GetComponent<UnitController>();
        unitCtrl.playerController = this;
        units.Add(unitCtrl);
    }

    public void AddClone()
    {
        if (units.Count >= 5) return;

        var clone = Instantiate(unitPrefab, transform.position, Quaternion.identity);
        var unitCtrl = clone.GetComponent<UnitController>();
        unitCtrl.playerController = this;
        units.Add(unitCtrl);
        UpdateFormation();
    }

    public void UnitDestroyed(UnitController unit)
    {
        units.Remove(unit);
        if (units.Count == 0)
        {
            Debug.Log("ゲームオーバー!");
            // ゲームオーバー処理をここに書く
        }
        else
        {
            UpdateFormation();
        }
    }

    void UpdateFormation()
    {
        // フォーメーション再計算・配置処理
    }
}

まとめ
✅ 主機と分身を区別しない設計にできます。
✅ PlayerController が全体を管理し、誰が壊れても数を減らす。
✅ 残り0機のときだけゲームオーバー。

最初からある機体とその他を区別しようとしていたので、変更をお願いしてみました。

コードをパッと見て分からなかった部分も聞いてみます。

自機は右側を向いているのですが、その右向きの自機に対して相対座標を取って分身を増やしていく設計になっていますか?

ChatGPTの回答(クリックで展開)
とても良い質問です!整理してお答えします。

🌟 重要ポイントの整理
✅ 自機は右向き(+X方向)である。
✅ 分身を増やすときの相対座標(例:「右」「左」「右後ろ」「左後ろ」)は、
 この右向きを基準に計算されているか?という質問ですね。

🛠 結論(現状の設計)
現状のコード案では:
✅ 自機が右向き(+X方向)前提の相対座標で設計されています。

具体例:
- 右側に分身 → +1 on X
- 左側に分身 → -1 on X
- 右後ろ → +0.5 on X, -1 on Y
- 左後ろ → -0.5 on X, -1 on Y

つまり、
✅ 自機が常に右を前として動くゲームなら、このままの設計で問題ありません。

補足(向き対応を汎用化したい場合)
将来、自機の向き(Rotation)に応じて相対座標を計算したい場合は、
以下のように transform.righttransform.up を使って相対座標を求める方式に変更するのがおすすめです。

問題なかったようです。自機の位置の設定をまとめてみると以下の通りでしょうか。

C#メモ
コード 意味
List<Vector2> positions = GetFormationPattern(unitCount); 相対座標のリストを作る
positions.Add(Vector2.zero); 自機の位置に (0,0) の Vector2 を設定
if (unitCount >= 2) positions.Add(new Vector2(1, 0)); 自機が2機以上であれば対応した Vector2 を設定
transform.localPosition = positions[0]; 最初の自機の位置を相対座標で設定
clones[i].transform.localPosition = transform.position + (Vector3)positions[i + 1]; 増えた自機の座標を positions[] に対応して設定

あとは、ゲーム画面でテストするときに操作していたので分かりましたが移動キーと新しく設定しようとしている隊列変更のキーが被っていることを確認しています。

隊列の変更をQ,W,Eで実装しようとしていますが、WとSは垂直方向に動くためのキーとして設定してあります。

ChatGPTの回答(クリックで展開)
とても良い指摘・確認です!ゲーム設計として、移動キーと隊列切り替えキーがバッティングしないのはとても重要です。

🌟 現状の課題整理
✅ 移動キー:
- W → 上方向移動
- S → 下方向移動
- A / D → 左右移動

✅ 隊列切り替えキー(予定):
- Q → 元の隊列
- W → 縦一列隊列(← ここが移動とバッティング)
- E → 横一列隊列

問題点
Wキーが移動と隊列切り替えの両方に使われていて、ゲーム中に操作が競合する。

解決策案
1️⃣ 隊列切り替えを別キーに移す(おすすめ)
- 元の隊列 → Q
- 縦一列隊列 → R(WからRに変更)
- 横一列隊列 → E
→ Q, R, E は移動と独立していて押しやすい。

2️⃣ 他の候補
- 元の隊列 → 1
- 縦一列隊列 → 2
- 横一列隊列 → 3
→ 数字キーは移動と完全独立なので誤操作が少ない。

🛠 コード修正例

void Update()
{
    if (Input.GetKeyDown(KeyCode.Q))
    {
        formation = "original";
        UpdateFormation();
    }
    if (Input.GetKeyDown(KeyCode.R)) // ← WからRに変更
    {
        formation = "vertical";
        UpdateFormation();
    }
    if (Input.GetKeyDown(KeyCode.E))
    {
        formation = "horizontal";
        UpdateFormation();
    }
}

まとめ
移動用キー(W, A, S, D)と隊列切り替えキーは分けるべき。
おすすめは:
→ Q(元)、R(縦)、E(横)または 1 / 2 / 3。

どちらかというとQ,E,Rで離れているよりは1,2,3で連続している方が好きなのでそちらでお願いしました。

設計案確定~実装

ここまで確認してきたもので実装していきます。

自機の増減について、ここまで確認してきたものを変更して最終的に実装するべきものをまとめてください

ChatGPTの回答(クリックで展開)
最終まとめ:自機の増減&隊列システム

🛡 要件整理
1️⃣ 最大5機まで(主機+分身)

2️⃣ 増加順
1体目:主機(初期)
2体目以降:右 → 左 → 右後ろ → 左後ろ(右向き前提、相対座標)

3️⃣ 減少時の挙動
どの機体が破壊されても隊列数を1つ減らす。
リスト最後の機体を消し、隊列を再計算。
0機になったらゲームオーバー。

4️⃣ 隊列切り替え(フォーメーション)
数字キー 1 → 元の隊列(original)
数字キー 2 → 縦一列(vertical)
数字キー 3 → 横一列(horizontal)

5️⃣ 主機と分身に区別しない
全員同じ UnitPrefab を使用。
プレイヤー管理クラス(PlayerController)が全体を統括。

🛠 実装の設計方針
✅ PlayerController
 全機体(自機・分身)のリスト管理。
 増減・位置再計算・ゲームオーバー判定を担当。

✅ UnitController
 自分が被弾したら PlayerController に「やられた」と通知。

✅ 座標配置
 original: (0,0), (1,0), (-1,0), (0.5,-1), (-0.5,-1)(主機基準、右向き前提)
 vertical: (0,0), (0,-1), (0,-2), (0,-3), (0,-4)
 horizontal: (0,0), (1,0), (2,0), (3,0), (4,0)

📦 実装項目まとめ
✅ 1️⃣ PlayerController.cs
 List<UnitController> units
 public GameObject unitPrefab
 public void AddUnit()
 public void UnitDestroyed(UnitController unit)
 private void UpdateFormation()

✅ 2️⃣ UnitController.cs
 public PlayerController playerController
 OnTriggerEnter2D()で被弾判定 → playerController.UnitDestroyed(this)

✅ 3️⃣ 隊列切り替え(数字キー1/2/3)
 Input.GetKeyDown(KeyCode.Alpha1) などで formation 切り替え。
 切り替え後 UpdateFormation() 実行。

✅ 4️⃣ ゲームオーバー判定
 units.Count == 0 でゲームオーバー。

補足(必要なら追加できるもの)
✅ 各分身のスムーズな移動(Lerp補間)
✅ 攻撃処理をPlayerController経由でまとめる
✅ フォーメーションの視覚エフェクト
✅ ゲームオーバー画面やUI連携

    🛠 PlayerFormationManager.cs(概略)

using System.Collections.Generic;
using UnityEngine;

public class PlayerFormationManager : MonoBehaviour
{
    public GameObject unitPrefab;
    public List<UnitController> units = new List<UnitController>();
    public string formation = "original";
    public int maxUnits = 5;

    void Start()
    {
        var mainUnit = Instantiate(unitPrefab, transform.position, Quaternion.identity).GetComponent<UnitController>();
        mainUnit.formationManager = this;
        units.Add(mainUnit);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1)) { formation = "original"; UpdateFormation(); }
        if (Input.GetKeyDown(KeyCode.Alpha2)) { formation = "vertical"; UpdateFormation(); }
        if (Input.GetKeyDown(KeyCode.Alpha3)) { formation = "horizontal"; UpdateFormation(); }
    }

    public void AddUnit()
    {
        if (units.Count >= maxUnits) return;
        var newUnit = Instantiate(unitPrefab, transform.position, Quaternion.identity).GetComponent<UnitController>();
        newUnit.formationManager = this;
        units.Add(newUnit);
        UpdateFormation();
    }

    public void UnitDestroyed(UnitController unit)
    {
        units.Remove(unit);
        Destroy(unit.gameObject);
        if (units.Count == 0)
        {
            Debug.Log("ゲームオーバー!");
            // ゲームオーバー処理
        }
        else
        {
            UpdateFormation();
        }
    }

    void UpdateFormation()
    {
        List<Vector2> pattern = GetFormationPattern(units.Count);
        for (int i = 0; i < units.Count; i++)
        {
            units[i].transform.localPosition = (Vector3)pattern[i];
        }
    }

    List<Vector2> GetFormationPattern(int count)
    {
        var positions = new List<Vector2>();
        if (formation == "original")
        {
            positions.Add(Vector2.zero);
            if (count >= 2) positions.Add(new Vector2(1, 0));
            if (count >= 3) positions.Add(new Vector2(-1, 0));
            if (count >= 4) positions.Add(new Vector2(0.5f, -1));
            if (count >= 5) positions.Add(new Vector2(-0.5f, -1));
        }
        else if (formation == "vertical")
        {
            for (int i = 0; i < count; i++) positions.Add(new Vector2(0, -i));
        }
        else if (formation == "horizontal")
        {
            for (int i = 0; i < count; i++) positions.Add(new Vector2(i, 0));
        }
        return positions;
    }
}

🛡 UnitController.cs(対応変更)

using UnityEngine;

public class UnitController : MonoBehaviour
{
    public PlayerFormationManager formationManager;

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Enemy"))
        {
            formationManager.UnitDestroyed(this);
        }
    }
}

補足(必要なら追加できるもの)
✅ 各分身のスムーズな移動(Lerp補間)
✅ 攻撃処理をFormationManager経由でまとめる
✅ フォーメーションの視覚エフェクト
✅ ゲームオーバー画面やUI連携

仕様とスクリプトをまとめてくれていますが、実装の手順について間違える可能性が高いので教えてもらいましょう。

スクリプトの作成以外に行うべき作業を教えてください

ChatGPTの回答(クリックで展開)

とても良い質問です!
スクリプトを完成させただけでは Unity では機能しないことが多いので、
必要な Unityエディタ側の作業(設定・配置・確認) をまとめます。

以下に、今回の自機増減&フォーメーションシステムに関して
スクリプト以外で行うべき作業をリスト化しました。

✅ Unityエディタ側で行うべき作業
🛠 1️⃣ プレハブ(Prefab)の用意
UnitPrefab(自機・分身共通プレハブ)

SpriteRenderer:自機画像。

Rigidbody2D(推奨:Kinematic)。

Collider2D(推奨:IsTrigger = true)。

UnitController スクリプトをアタッチ。

必要ならアニメーションやエフェクトも。

🛠 2️⃣ FormationManagerオブジェクトの配置
Hierarchy に空の GameObject を作成。
例:PlayerFormationManager

PlayerFormationManager スクリプトをアタッチ。

Inspector で UnitPrefab に 上記で作成した UnitPrefab をドラッグ。

🛠 3️⃣ PlayerFormationManager の初期配置
PlayerFormationManager の Transform.position をゲーム開始時の主機位置にセット。

例えば (0, 0, 0) や右端近く ( -7, 0, 0 )。

🛠 4️⃣ Tag の設定
敵オブジェクトに Enemy タグを設定。

(必要なら)弾オブジェクトにもタグ設定。

🛠 5️⃣ カメラ・画面枠の調整
分身が増えると横・縦方向に広がるので、
ゲーム画面内で見える位置・範囲か確認。

カメラのSize(Orthographic Size)や位置を調整。

🛠 6️⃣ 入力設定の確認
数字キー1/2/3は Input.GetKeyDown(KeyCode.Alpha1) で直接読んでいるので特別な設定は不要。

もし新Input Systemを使う場合は、別途InputActionの設定が必要。

🛠 7️⃣ 敵やその他との接触テスト
敵Prefabを配置し、
分身・主機に当たったとき OnTriggerEnter2D が発火するか確認。

🌟 (オプション)おすすめの追加作業
✅ UI表示
現在の隊列や残り機数を画面に表示(TextMeshPro推奨)。

✅ 破壊エフェクト・音
分身が消えるときやゲームオーバー時にエフェクト・音を鳴らす。

✅ ゲームオーバー画面
残り機数0で一時停止&リスタートやタイトル画面遷移。

✅ 最終チェックリスト(短縮版)
✅ UnitPrefabの作成・設定。
✅ PlayerFormationManagerの配置・設定。
✅ Tag設定(敵や弾)。
✅ カメラ・画面枠の調整。
✅ 入力・操作確認。
✅ 当たり判定の実機テスト。


PlayerのPrefabにUnitController


FormationMangerの各設定



一通りの設定が完了しました。
動かしてみましょう。

見た目もおかしいですが、自機が増えるはずなのに増えていません。
これの修正も続けてやりたいところですが手強い感じがするので今回はここまでで区切りたい思います。お疲れさまでした。

◇ 関連記事
アイテムの実装はこちら

  • この記事を書いた人

たげお

ソロゲームもオンラインゲームも対戦ゲームもソシャゲも楽しむ雑食ゲーマー。特筆して好きなジャンルはRPG、FPS。最近は格ゲーにお熱。 思い出のゲームは『FF9』

-Unity, ゲーム開発, 生成AI
-, , ,