Unity ゲーム開発 生成AI

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

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

記事の目的

前回、アイテムによって自機が増加する仕組みの実装を行いました。
前回記事 ⇒ #9_アイテムの実装②


しかし、思うような挙動にならなかったため修正する必要があります。

アイテムの実装

修正

オブジェクト向き修正

まず、自機が上向きになっているのを直します。
これについては生成時に回転が設定されていないような気がします。


Quatertion.Identity になっていたコードを Quatertion.Euler(0,0,-90) に変更。

これでOKです。


自機が増加しない問題の修正

次に自機が増加しない問題を解決していきたいと思います。

Dubug.Logには自機が増加と出るのですが、ゲーム上では自機が増加しません。原因を考えてください

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

とても良い質問です。
この現象を整理するとこうです:

🌟 状況整理
コンソールには Debug.Log("自機増加! 現在の分身数: X"); と出る。
だがゲーム画面上では実際に自機(分身)が増えない。

つまり:
✅ AddClone()(または AddUnit())は 呼ばれている。
❌ でも 画面に見える分身は増えない。

🛠 考えられる主な原因
1️⃣ 実体の生成をしていない/間違っている
例:


cloneCount += 1;
Debug.Log("自機増加! 現在の分身数: " + cloneCount);
        

このように数値を増やすだけで、
Instantiate(unitPrefab) で分身の実体を生成していない。

2️⃣ PlayerController の AddClone() と PlayerFormationManager の AddUnit() が混同されている
もし PlayerController 内の AddClone() だけを呼んでいて、
実際の分身管理・生成を担当する PlayerFormationManager の AddUnit() を呼んでいない場合。

3️⃣ unitPrefab が設定されていない or null
PlayerFormationManager の Inspector で unitPrefab が空欄のままだと、
Instantiate 時に何も作れない。

4️⃣ 生成した分身が画面外 or 非表示
例えば:
・座標 (0,0) ではなく (9999,9999) に生成されている。
・スケール (0,0,0) で表示されない。
・カメラ範囲外で見えていない。

5️⃣ 分身の UpdateFormation() が呼ばれていない/失敗している
生成後に位置を再計算・配置する UpdateFormation() が動いておらず、
分身が主機と重なったまま埋もれて見えない、など。

チェック項目リスト(順番に確認してください)
1️⃣ 分身を生成するコードが実際にあるか?
例(PlayerFormationManager 側):


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();
}
        

2️⃣ PlayerController から PlayerFormationManager.AddUnit() を呼んでいるか?
PlayerController 側の AddClone() から単に cloneCount++ だけで終わっていないか確認。
正しくは:


formationManager.AddUnit();
        

3️⃣ unitPrefab が正しく Inspector で登録されているか?
Hierarchy 上の PlayerFormationManager オブジェクトを選び、
Inspector の unitPrefab スロットに分身Prefabをドラッグ済みか確認。

4️⃣ UpdateFormation() で配置しているか?
分身生成後、units リストに追加したあとに位置を再配置しているか。

5️⃣ 生成位置が正しいか?
生成時に transform.position など主機の近くに生成されているか確認。

🚀 確認後の修正例
もし PlayerController 側から PlayerFormationManager 側を呼び出していないなら、
以下のようにする必要があります:


public class PlayerController : MonoBehaviour
{
    public PlayerFormationManager formationManager;

    public void AddClone()
    {
        formationManager.AddUnit();
        Debug.Log("自機増加! 現在の分身数: " + formationManager.units.Count);
    }
}
        

🔧 まとめ
✅ Debug.Log が出る=関数は呼ばれている。
✅ でも 画面に増えない=Prefab生成や配置処理が抜けている or 動いていない。

もしよければ:
✅ あなたの PlayerController と PlayerFormationManager の実コードを貼ってもらえれば、
具体的に「ここが足りない」とピンポイント修正します!

コードを貼ってもいいそうですが、ひとまず確認していきます。

1️⃣ 分身を生成するコードが実際にあるか?

そのまま入れたので大丈夫そうです。

2️⃣ PlayerController から PlayerFormationManager.AddUnit() を呼んでいるか?

ここっぽいですね。言われているとおりカウントだけして止まっていそうです。
適当にChatGPTに投げてみます。

public void AddClone()
{
    cloneCount += 1;
    Debug.Log("自機増加! 現在の分身数: " + cloneCount);
    // 例:FormationManager.instance.AddClone();
}

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

とても良い気づきです!
この部分が原因で「ログは出るのに実際の分身が増えない」という問題が起きています。

🌟 現状の問題
あなたの PlayerController.cs の AddClone() はこうなっています:


public void AddClone()
{
    cloneCount += 1;
    Debug.Log("自機増加! 現在の分身数: " + cloneCount);
    // 例:FormationManager.instance.AddClone();
}
        

✅ 今やっていること
・cloneCount という数値変数を増やす。
・Debug.Log でメッセージを出す。

❌ やっていないこと
・実際にゲーム内に分身(UnitPrefab)を Instantiate する。
・PlayerFormationManager(隊列管理側)に「分身を増やして!」と命令する。

🛠 改善するべき設計
PlayerController 側は、
自分で cloneCount を増やすのではなく、
分身管理担当(PlayerFormationManager)に増加処理を依頼する。

具体的に:


public PlayerFormationManager formationManager;

public void AddClone()
{
    formationManager.AddUnit();
}
        

そして PlayerFormationManager.cs の AddUnit() が:


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();
    Debug.Log("自機増加! 現在の分身数: " + units.Count);
}
        

のように、実際の生成・管理を行います。

必要な作業まとめ
1️⃣ PlayerController.cs に PlayerFormationManager 参照を持たせる


public PlayerFormationManager formationManager;
        

Unityエディタで、
Hierarchy上の PlayerFormationManager オブジェクトを PlayerController の FormationManager スロットにドラッグ登録。

2️⃣ AddClone() 内で formationManager.AddUnit() を呼ぶ


public void AddClone()
{
    formationManager.AddUnit();
}
        

3️⃣ PlayerFormationManager.cs 側で実際の分身生成を行う
・Instantiate(unitPrefab)
・unitsリスト追加
・UpdateFormation() 呼び出し

🚀 結論
✅ 今の AddClone() は数値だけ増やしている → ゲーム上では増えない。
✅ 分身の実体管理(生成・配置)は PlayerFormationManager 側に移すべき。
✅ PlayerController からは formationManager.AddUnit() を呼び出す設計にする。

PlayerContorollerのAddCloneを書き換え
PlayerContorollerにFormationMangerの参照を作成
FormationMangerのAddUnitを書き換え

この中で「Unityエディタで、Hierarchy上の PlayerFormationManager オブジェクトを PlayerController の FormationManager スロットにドラッグ登録。」の部分ができませんでした。

Unityエディタで、
Hierarchy上の PlayerFormationManager オブジェクトを PlayerController の FormationManager スロットにドラッグ登録。

アタッチできませんまた、オブジェクト名はPlayerFormationManagerではなくFormationMangerとしています

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

とても良い情報ありがとうございます!
あなたの状況を整理すると:

🌟 現状の状況
✅ オブジェクト名
Hierarchy上のオブジェクト名は → FormationManager

✅ スクリプト名(クラス名)
おそらく:


public class FormationManager : MonoBehaviour
        

✅ PlayerControllerスクリプト内の定義


public PlayerFormationManager formationManager;
        

問題の原因
UnityのInspectorでスロットにドラッグできない原因は、
スクリプト側の型とオブジェクトにアタッチされているスクリプトの型が違うからです。

具体的には:
PlayerController側では PlayerFormationManager 型を要求。
でも Hierarchy上の FormationManager オブジェクトには
FormationManager という別のスクリプトがついている。

これだと Unity の型チェックに通らず、ドラッグ登録できません。

解決策
次のどちらかの方法で統一してください。

方法①:クラス名を FormationManager に統一
FormationManager.cs のスクリプトを次のように修正。


public class FormationManager : MonoBehaviour
{
    // ここに元の PlayerFormationManager の内容を書く
}
        

そして PlayerController.cs 側も:


public FormationManager formationManager;
        

クラス名と参照型名が一致していれば、Inspectorにドラッグ可能になります。

どこかでPlayerFormationManagerとFormationMangerで名前が混同していたのを修正しました。
ところがこれでも治らず依然アタッチ不可能です。根気強く改善を試みます。

アタッチできません

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

了解しました!
ここからは一緒に「なぜFormationManagerをPlayerControllerのInspectorにアタッチできないのか」を絞り込んでいきましょう。

チェックポイント(順番に確認)
1️⃣ FormationManagerオブジェクトにFormationManagerスクリプトが付いているか?
Hierarchyで FormationManager というオブジェクトを選び、
Inspectorを見てください。

✅ Inspectorに「FormationManager (Script)」というコンポーネントが表示されていますか?

もし表示されていない場合:
FormationManager.cs を Projectビューから Hierarchy上の FormationManagerオブジェクトにドラッグして追加する。

2️⃣ FormationManager.csのクラス名とファイル名が一致しているか?
開発中によくあるのが:

ファイル名は FormationManager.cs だが、
中のクラス名が間違っている例。

確認してください:


public class FormationManager : MonoBehaviour
        

もしクラス名が public class PlayerFormationManager などになっていたら、
クラス名を FormationManager に修正するか、PlayerController側の型を合わせてください。

3️⃣ PlayerController.csの参照型がFormationManagerになっているか?
あなたのPlayerControllerは今:


public FormationManager formationManager;
        

になっているので、Inspector側では FormationManager型のオブジェクトのみがドラッグ対象になります。

もしFormationManager.csのクラス名が PlayerFormationManager なら、ここも合わせないとダメです。

4️⃣ スクリプトコンパイルエラーがないか?
Unity上部のコンソール(赤×マーク)を確認してください。

もしスクリプトにコンパイルエラーがあると、
Unityはそのスクリプトを型としてInspectorで認識できず、ドラッグできません。

例:
・綴りミス
・クラス名とファイル名の不一致
・コンパイルエラー

があれば、まずそれを修正してください。

5️⃣ アタッチ対象が正しいか?
HierarchyからPlayerオブジェクトを選び、
そのInspector内のFormationManagerスロットに、
Hierarchy内の FormationManagerオブジェクト自体をドラッグしていますか?

ProjectビューのFormationManager.cs(スクリプトファイルそのもの)を
直接ドラッグしていませんか?
→ これはNG。必ずHierarchy上のオブジェクトをドラッグします。

🛠 確認手順まとめ
✅ HierarchyにFormationManagerオブジェクトがある。
✅ そのInspectorにFormationManagerスクリプトがついている。
✅ FormationManager.csのクラス名はFormationManager。
✅ PlayerController.csの参照はFormationManager。
✅ スクリプトにコンパイルエラーがない。
✅ Projectビューのスクリプトではなく、Hierarchy上のオブジェクトをドラッグしている。

5️⃣ アタッチ対象が正しいか? で問題がありました。
今までProjectのPrefabにアタッチすればいいと考えていましたが、HierarchyのPrefabにアタッチしなければならないとのこと。
しかし、これもまた問題がある気がします。

Playerは最初に生成するのでHierarchyには置けないのではないですか?

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

とても重要な指摘です!
ここで疑問が解けるように整理して説明します。

🌟 問題の核心
あなたの言う通りです:

✅ Player(主機)はゲーム開始時に Instantiate で生成している
→ Hierarchy上に事前には存在しない。
→ だから、Hierarchyの FormationManager オブジェクトを Player の Inspector にドラッグ登録できない。

これ、完全に正しいです。

UnityのInspectorの制約
Unityの Inspector で参照を設定できるのは:
・Scene(Hierarchy)上に存在する GameObject
・または Projectビューのアセット(Prefab、ScriptableObjectなど)

でもスクリプト内で生成される(Instantiateされる)オブジェクトは、
事前に Inspector から参照を設定することができません。

解決策
PlayerController の formationManager を
Inspectorで登録しない構造にする必要があります。

具体的には、生成後にスクリプトで代入します。

例:FormationManager 側から Player へ渡す
FormationManager.cs(Start内など)で:


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

    // PlayerControllerも取得してformationManagerを代入
    var playerController = mainUnit.GetComponent<PlayerController>();
    if (playerController != null)
    {
        playerController.formationManager = this;
    }

    units.Add(mainUnit);
}
        

例:Player側が自分で探す
PlayerController.cs内で:


void Start()
{
    if (formationManager == null)
    {
        formationManager = FindObjectOfType<FormationManager>();
    }
}
        

これで、Hierarchy上の FormationManager オブジェクトを自動で探して代入します。

生成されたものを後で入れる処理ができるようです。
FormationManagerのstartでPlayerContorollerを取得→FormationManagerに生成した自機を入れるという処理になっていそうです。

これで一応自機が増えるようにはなりましたが以下のGIFのような状態になりました。

自機が増えるとき(おそらく隊列が変更されたとき)に中央に自機が移動します。
今のスクリプトをあまり理解できていないので聞いていきます。

自機が増えるとき(おそらく隊列が変更されたとき)に中央に自機が移動します。

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

とても重要な現象の報告ありがとうございます!
内容を整理すると:

🌟 現象整理
自機が増えるとき、または隊列変更時、主機(Player)が中央(位置0,0)にワープする。
意図していない「位置リセット」が発生している。

原因の可能性(ほぼ特定)
FormationManager.cs のこの部分:


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

ここで 主機(最初の1体)にも相対座標を強制的に適用している のが原因です。

特に:
pattern[0] が Vector2.zero なので、
主機の位置が毎回 (0,0) になってしまう。

つまり:
主機がすでにシーン内のどこかに配置されていても、
UpdateFormation() のたびに (0,0) にワープさせてしまっている。

🛠 改善方針
主機(Playerオブジェクト)の位置は変えず、
分身だけを主機を基準に配置する ように変更します。

修正版例
FormationManager.cs の UpdateFormation() を以下のように修正:


void UpdateFormation()
{
    List<Vector2> pattern = GetFormationPattern(units.Count);
    Vector3 mainPosition = units[0].transform.position;  // 主機の現在位置

    // 主機(units[0])の位置は固定、分身のみ配置
    for (int i = 1; i < units.Count; i++)
    {
        units[i].transform.position = mainPosition + (Vector3)pattern[i];
    }
}
        

ポイント:
✅ 主機(index 0)の位置をそのまま使う。
✅ 分身(index 1以降)だけに相対位置を設定。

FormationManager内で最初の自機に対して(0,0)に動け!という命令になっていたらしいです。
これを最初の自機からの相対座標に変更することで解決しました。

ここで、ゲーム中でエラーが発生しています。

NullReferenceException: Object reference not set to an instance of an object
PlayerController.AddClone () (at Assets/Script/Player/PlayerController.cs:37)
Item.StrengthenPlayer () (at Assets/Script/Item/Item.cs:58)
Item.TakeDamage (System.Int32 amount) (at Assets/Script/Item/Item.cs:39)
Bullet.OnTriggerEnter2D (UnityEngine.Collider2D collision) (at Assets/Script/Bullet/Bullet.cs:28)

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

とても良い報告です!
これで何が起きているかがはっきりしました。

🌟 エラー内容を整理


NullReferenceException: Object reference not set to an instance of an object
PlayerController.AddClone () (at Assets/Script/Player/PlayerController.cs:37)
        

要するに:
PlayerController.AddClone() の中で null のオブジェクトを参照しようとした。

具体的には PlayerController.cs の 37行目:


formationManager.AddUnit();
        

この formationManager が null です。

つまり:
PlayerController の formationManager 変数に、
FormationManager オブジェクトが代入されていない状態で AddClone() が呼ばれた。

なぜ null なのか?
先ほどのやり取りでわかってきた通り:

✅ Player(PlayerControllerがついているオブジェクト)はゲーム開始時に Instantiate で生成される。
✅ だから Unity Editor の Inspector で formationManager をドラッグ登録できない。

結果 → 生成後の PlayerController では formationManager が null のまま。

🛠 解決策
スクリプト側で自動的に formationManager を見つけて代入するようにしましょう。

修正例(PlayerController.cs)


void Start()
{
    if (formationManager == null)
    {
        formationManager = FindObjectOfType<FormationManager>();
    }
}
        

これを PlayerController に追加。

意味:
Scene内の FormationManager を自動的に探し、代入する。

これで Inspector登録が不要になり、null参照が消える。

注意点
Scene内に FormationManager が複数あると最初の1つを拾うので要注意。
必ず Hierarchy に FormationManager オブジェクトが1つ配置されていること。

🚀 確認後の流れ
✅ PlayerController.cs に上記 Start() を追加。
✅ Hierarchy 上に FormationManager オブジェクトを1つ配置。
✅ FormationManager に FormationManager.cs をアタッチ済み。

これで動作するはずです。

PlayerControllerでスタート時にFormationManagerを見つけるようにしました。

エラーの読み取り
コード/エラー 意味
NullReferenceException Null を参照しようとした(オブジェクトが設定されていない状態)
Object reference not set to an instance of an object 上と同じ意味(Null参照エラー)
PlayerController.AddClone() PlayerController クラスの AddClone() メソッド内で発生
at Assets/Script/Player/PlayerController.cs:37 PlayerController.cs の37行目でエラーが発生

修正②

再度動かしてみます。

ここでも問題がいくつか発生しました。

  • 隊列が上方向を基準にして生成されている。
  • 後ろの自機が銃弾を打つと前の自機に当たって消えている。
  • 自機ごとに強化が保存されている?

生成位置

まず、生成位置について

上方向が基準というよりワールド座標を基準にしているようなので余計なコードの変更は行わずに、座標の数値のみを書き換えました。

銃弾の干渉

次、銃弾と自機の干渉について

後ろの自機が銃弾を打つと前の自機に当たって消えています。どのような修正が考えられますか

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

とても重要な質問です!
これは多機体(主機+分身)シューティングでよく出る「味方同士の当たり判定」の問題です。

以下に問題の整理と解決案を説明します。

🌟 問題の整理
現状起きていること:
✅ 後ろの自機(分身)が弾を撃つ。
✅ その弾が前の自機(主機や分身)に当たる。
✅ 味方を敵と誤認して弾が消滅、またはダメージ処理が走る。

原因
これは「当たり判定の対象を正しく分けていない」のが原因です。
Unityでは OnTriggerEnter2D() や OnCollisionEnter2D() の中で、
相手のタグやレイヤーを確認しないと、味方にも当たる判定になってしまいます。

🛠️ 改善案(例)
自機(Player)に当たった場合は何もしないようにする。


void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.CompareTag("Player"))
    {
        return;  // 自機に当たったら何もしない
    }

    if (collision.CompareTag("Item"))
    {
        var item = collision.GetComponent<Item>();
        if (item != null)
        {
            item.TakeDamage(damage);
        }
    }

    if (collision.CompareTag("Enemy"))
    {
        var enemy = collision.GetComponent<Enemy>();
        if (enemy != null)
        {
            enemy.TakeDamage(damage);
        }
    }

    Destroy(gameObject);
}
        

補足
タグではなく「Layer」を使って Physics2D 設定から「PlayerとPlayerBulletは当たらない」ように
レイヤーマスクで完全分離するのも強力な方法です。

Playerのタグを持っている場合には何も起こらずに飛んでいくようにしました。

自機の強化状態

次に自機の強化状態について
現在の5機まで増えたときの挙動がこれです。

明らかに発射間隔が短い自機がいます。
自機ごとに強化の状態が違うように見受けられますね。

これについてはかなり紆余曲折があり修正に3日かかりました。
最終的にはChatGPTからこことここを直して~と言われていたところとは別のところに修正のポイントがあったことが主な要因でした。

3日間ChatGPTのチャットで修正案を出させ続けましたが結局コードを理解するまで読み直してみたらすぐにおかしい部分が見つかりました。


自分には分かるようにまとめた銃弾が飛んでいくまでの仕組み

銃弾1個が出ていくにもあっちこっち参照していて理解できていませんでした。
しかし、この図を作ろうとしたところChatGPTが完全にItem.csの存在を忘れていることに気づきました。

コードを自分だけで書く必要は無いとは相変わらず考えていますが、
ChatGPTが出力したコードは自分用だけでいいのでまとめておくのが無難かなと思っています。

細かい話は抜きにしても教訓になったことはあります。

ChatGPTが出してきたコードは理解しよう

ということです。

とりあえずコードの中身がここで大なり小なり変わってしまったのでアイテムの実装に関わるコードを貼り付けておこうと思います。

Item.cs(クリックで展開)

using UnityEngine;

public class Item : MonoBehaviour
{
    public enum ItemType { PowerUp, RapidFireUp, AddClone }
    public ItemType itemType;
    public int maxHP = 10;
    private int currentHP;
    public float speed = 2f;

    private void Start()
    {
        currentHP = maxHP;
    }

    private void Update()
    {
        transform.position += Vector3.left * speed * Time.deltaTime;
    }

    public void TakeDamage(int amount)
    {
        currentHP -= amount;
        if (currentHP <= 0)
        {
            StrengthenPlayer();
            Destroy(gameObject);
        }
    }

    private void StrengthenPlayer()
    {
        var playerManager = FindObjectOfType<PlayerManager>();
        if (playerManager != null)
        {
            switch (itemType)
            {
                case ItemType.PowerUp:
                    playerManager.IncreasePower();
                    break;
                case ItemType.RapidFireUp:
                    playerManager.IncreaseFireRate();
                    break;
                case ItemType.AddClone:
                    playerManager.AddClone();
                    break;
            }
        }
    }
}
        

FormationManager.cs(クリックで展開)

using System.Collections.Generic;
using UnityEngine;

public class FormationManager : MonoBehaviour
{
    public GameObject unitPrefab;
    public PlayerManager playerManager;
    public int maxUnits = 5;

    private List<UnitController> units = new List<UnitController>();
    private float fireTimer = 0f;
    private string currentFormation = "original";

    void Start()
    {
        SpawnMainUnit();
    }

    void Update()
    {
        float currentFireRate = playerManager.fireRate;

        if (Input.GetButtonDown("Fire1"))
        {
            ShootAll();
            fireTimer = 0f;
        }
        else if (Input.GetButton("Fire1"))
        {
            fireTimer += Time.deltaTime;
            if (fireTimer >= currentFireRate)
            {
                ShootAll();
                fireTimer = 0f;
            }
        }
        else
        {
            fireTimer = 0f;
        }

        if (Input.GetKeyDown(KeyCode.Alpha1)) UpdateFormation("original");
        if (Input.GetKeyDown(KeyCode.Alpha2)) UpdateFormation("vertical");
        if (Input.GetKeyDown(KeyCode.Alpha3)) UpdateFormation("horizontal");
    }

    void SpawnMainUnit()
    {
        var mainUnit = Instantiate(unitPrefab, transform.position, Quaternion.Euler(0, 0, -90)).GetComponent<UnitController>();
        mainUnit.Initialize(playerManager);  // ← 修正済み(1引数)
        units.Add(mainUnit);
    }

    public void AddUnit()
    {
        if (units.Count >= maxUnits) return;

        var newUnit = Instantiate(unitPrefab, transform.position, Quaternion.Euler(0, 0, -90)).GetComponent<UnitController>();
        newUnit.Initialize(playerManager);
        units.Add(newUnit);
        UpdateFormation(currentFormation);
    }

    public void UnitDestroyed(UnitController unit)
    {
        units.Remove(unit);
        Destroy(unit.gameObject);

        if (units.Count == 0)
        {
            Debug.Log("全滅!ゲームオーバー!");
            // TODO: ゲームオーバー処理
        }
        else
        {
            UpdateFormation(currentFormation);
        }
    }

    void ShootAll()
    {
        foreach (var unit in units)
        {
            unit.Shoot();
        }
    }

    void UpdateFormation(string formation)
    {
        currentFormation = formation;
        Vector3 mainPos = units.Count > 0 ? units[0].transform.position : transform.position;
        Vector3 right = Vector3.right;
        Vector3 up = Vector3.up;

        List<Vector2> pattern = GetFormationPattern(units.Count, formation);
        for (int i = 0; i < units.Count; i++)
        {
            Vector2 pos = pattern[i];
            units[i].transform.position = mainPos + right * pos.x + up * pos.y;
        }
    }

    List<Vector2> GetFormationPattern(int count, string formation)
    {
        var positions = new List<Vector2> { Vector2.zero };

        if (formation == "original")
        {
            if (count >= 2) positions.Add(new Vector2(0, -1));
            if (count >= 3) positions.Add(new Vector2(0, 1));
            if (count >= 4) positions.Add(new Vector2(-1, -0.5f));
            if (count >= 5) positions.Add(new Vector2(-1, 0.5f));
        }
        else if (formation == "vertical")
        {
            if (count >= 2) positions.Add(new Vector2(-0.75f,0));
            if (count >= 3) positions.Add(new Vector2(0.75f,0));
            if (count >= 4) positions.Add(new Vector2(-1.5f,0));
            if (count >= 5) positions.Add(new Vector2(1.5f,0));
        }
        else if (formation == "horizontal")
        {
            if (count >= 2) positions.Add(new Vector2(0,-0.75f));
            if (count >= 3) positions.Add(new Vector2(0,0.75f));
            if (count >= 4) positions.Add(new Vector2(0,-1.5f));
            if (count >= 5) positions.Add(new Vector2(0,1.5f));
        }
        return positions;
    }
}
        

Bullet.cs(クリックで展開)

using UnityEngine;

public class Bullet : MonoBehaviour
{
    public int damage = 1;
    public float speed = 20f;
    private Rigidbody2D rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Start()
    {
        rb.velocity = transform.up * speed;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Enemy"))
        {
            var enemy = collision.GetComponent<Enemy>();
            if (enemy != null)
            {
                enemy.TakeDamage(damage);
            }
            Destroy(gameObject);
            return;  // Enemyに当たったら終了
        }

        if (collision.CompareTag("Item"))
        {
            var item = collision.GetComponent<Item>();
            if (item != null)
            {
                item.TakeDamage(damage);
            }
            Destroy(gameObject);
            return;  // Itemに当たったら終了
        }
    }
}
        

PlayerManager.cs(クリックで展開)

using UnityEngine;

public class PlayerManager : MonoBehaviour
{
    public int bulletDamage = 1;
    public float fireRate = 0.5f;
    public FormationManager formationManager;

    public void IncreasePower()
    {
        bulletDamage += 1;
        Debug.Log($"[PlayerManager] Power UP! Damage is now {bulletDamage}");
    }

    public void IncreaseFireRate()
    {
        fireRate = Mathf.Max(0.1f, fireRate - 0.1f);
        Debug.Log($"[PlayerManager] Fire Rate UP! Interval is now {fireRate:F2} sec");
    }

    public void AddClone()
    {
        if (formationManager != null)
        {
            formationManager.AddUnit();
        }
        else
        {
            Debug.LogWarning("[PlayerManager] FormationManager not assigned!");
        }
    }
}
        

UnitController.cs(クリックで展開)

using UnityEngine;

public class UnitController : MonoBehaviour
{
    public PlayerManager playerManager;
    private PlayerShooting shooting;

    public void Initialize(PlayerManager pm)
    {
        playerManager = pm;
        shooting = GetComponent<PlayerShooting>();
        shooting.playerManager = pm;
    }

    public void Shoot()
    {
        shooting.Shoot();
    }

    public void TakeDamage(int damage)
    {
        Debug.Log($"[{gameObject.name}] Took {damage} damage!");
        // FormationManager には直接触れない設計
    }
}
        

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

using UnityEngine;

public class PlayerShooting : MonoBehaviour
{
    public GameObject bulletPrefab;
    public Transform firePoint;
    public PlayerManager playerManager;

    public void Shoot()
    {
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.Euler(0, 0, -90));
        Bullet bulletScript = bullet.GetComponent<Bullet>();
        if (bulletScript != null)
        {
            bulletScript.damage = playerManager.bulletDamage;
        }
    }
}
        

これでアイテムの実装は終了とします。

  • アイテムを破壊すると攻撃力UP、連射速度UP、自機の増加の3つの効果が発動
  • 自機は最大5機まで増加し、隊列を1,2,3のボタンで変更可能

お疲れさまでした。

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

  • この記事を書いた人

たげお

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

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