記事の目的
前回、脇道にそれてタイトル画面を作りましたが敵の実装に戻りたいと思います。
前回記事⇒ #6_タイトル画面作成
#5で実装した内容は…
- 敵が自機に向かって動く
- 弾で倒せる
- 自機に当たると自機にダメージ
しかし、現在画面に配置してあるものしか動かず、
また、自機に向かってくる敵の1種類しかおらずどうやってもゲーム性が生まれません。
今回はそのあたりを改善できるように新たに敵を実装していきます。
敵の実装②
敵の自動生成
まずは#5で提案されていた敵を生成する機能を作っていきます。
🔄 あとで自動出現にしたい場合
EnemySpawner.cs のようなスクリプトで一定時間ごとに Instantiate() すればOKです(必要なら作成例を出します)
これの解説をお願いします。
ChatGPTの回答(クリックで展開)
敵を自動的に一定時間ごとに出現(生成)させるために必要な処理を、EnemySpawner.cs というスクリプトで実装する方法を詳しく解説します。
🎯 目的
敵を手動で配置するのではなく、時間経過に応じて自動で生成したい
生成する敵のプレハブ(Prefab)はあらかじめ用意しておく
🧩 スクリプト構成:EnemySpawner.cs(基本版)
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab; // 生成する敵プレハブ
public float spawnInterval = 2f; // 何秒ごとに出現させるか
public Transform[] spawnPoints; // 敵の出現位置(複数可)
private float timer;
void Update()
{
timer += Time.deltaTime;
if (timer >= spawnInterval)
{
SpawnEnemy();
timer = 0f;
}
}
void SpawnEnemy()
{
// ランダムな出現位置から生成
int index = Random.Range(0, spawnPoints.Length);
Instantiate(enemyPrefab, spawnPoints[index].position, Quaternion.identity);
}
}
🔍 スクリプトの中身を解説
行 | 処理内容 |
---|---|
public GameObject enemyPrefab; | 出現させたい敵のPrefab(インスペクタで設定) |
public float spawnInterval = 2f; | 敵を何秒ごとに出現させるか(2秒間隔) |
public Transform[] spawnPoints; | 敵の出現可能な位置の配列(複数の空オブジェクトで設定) |
timer += Time.deltaTime; | フレームごとに経過時間を加算 |
if (timer >= spawnInterval) | 出現間隔を超えたら敵を出す |
Instantiate(...) | プレハブから敵を生成する |
🛠 Unity側の設定手順
① 空のオブジェクトを作る(EnemySpawner)
Hierarchyで右クリック → Create Empty → 名前を「EnemySpawner」
EnemySpawner.cs をアタッチ
② スクリプトのインスペクタ設定
・Enemy Prefab:敵のプレハブをドラッグ&ドロップ
・Spawn Interval:例:2.0
・Spawn Points:空のオブジェクトを作り、それらの Transform を配列に登録(例:5か所など)
③ 出現位置の例
「SpawnPoint1」「SpawnPoint2」などの空オブジェクトを作成して、画面の右端などに配置
EnemySpawner にそれらを配列として設定
✅ 実行結果
ゲーム開始後、一定間隔でランダムな位置から敵が1体ずつ出現します。
🧪 応用も可能(必要であれば)
応用 | 方法 |
---|---|
ウェーブ式出現 | StartCoroutine() で制御 |
出現数の制限 | 出現数をカウントして制限する |
出現パターンの切り替え | ステージ進行に応じて spawnInterval や enemyPrefab を変更 |
EnemySpawnerを作成していきます。
分かりやすいように5つ分のレーンを作っておいたのでここから生成されるようにしましょう。
SpawnPointを5つ分作成します。
yの値を少しずつ変更してアタッチ
EnemySpawnerにアタッチしたスクリプトからSpawnPointを5にしてそれぞれに設定
生成される敵のPrefabが下方向を向いていてどうにも直せなかったので聞いてみます。
PrefabのRotationは-90しているのですがどこが原因でしょうか?
ChatGPTの回答(クリックで展開)
🔍 敵が下向きに生成されてしまう主な原因
原因 | 解説 |
---|---|
✅ プレハブのRotationが -90° に設定されている | → Quaternion.identity で生成すると、Rotationが無視されて 0° に戻される |
✅ スクリプトで Quaternion.identity を指定している | → これは「回転なし(0°)」で生成することを意味します |
✅ プレハブ自体のデフォルトの向きが下を向いている | → 画像やオブジェクトの作りが下向き(通常の"右向き"ではない)になっている可能性 |
✅ 解決方法
方法①:プレハブ側で回転をゼロにしておき、スクリプトで回転させる
Instantiate(enemyPrefab, spawnPoint.position, Quaternion.Euler(0, 0, -90));
→ 常にZ軸方向に -90° 回転した状態で生成されます
方法②:プレハブのRotationを 0° に戻して、Spriteや画像自体を右向きに作り直す
Unityの2Dオブジェクトは「右向き」が標準です。
Sprite画像が下を向いていると、Rotation 0° でも見た目が下を向きます。
→ なので、できればSpriteは右向きにして、Rotation = 0 で生成できる状態にしておくと管理が楽です。
✅ どちらが良い?
方法 | おすすめ度 | 理由 |
---|---|---|
方法①:スクリプトで -90 回転して生成 | 👍 短期的に楽 | プレハブをいじらずに見た目だけ調整できる |
方法②:プレハブ自体を右向きに整える | 💯 長期的におすすめ | 弾の方向・移動の向きと統一しやすく、バグが減る |
🧪 補足:現在の生成コードの一部を確認してみましょう
例:
Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
ここを下記のように変えてください:
Instantiate(enemyPrefab, spawnPoint.position, Quaternion.Euler(0, 0, -90));
スクリプトを変更して90度回転させました。
これで敵が無限に出てくるようになりました。
EnemyBの追加
敵の種類を増やしていきます。
上のGIFで右下に映っているやつを動くようにしてあげましょう。
最初に実装していたEnemyAとは動きを変えます。
- 直線で動く
- Aより速く動く
- 破壊するには少し難しいくらいの体力
こんな感じでしょうか。
※実はこのあたりでかなり方向性が迷子になってきています。ゲーム性を作れない気がしている感じです。"方向性を決めておく"は次のゲームを作るときに覚えておきたいところです。
直線で動く
Aより速く動く
破壊するには少し難しいくらいの体力
この条件で敵を追加するための操作を教えてください
ChatGPTの回答(クリックで展開)
「直線で動く(追尾しない)」「A(既存の敵)より速い」「少し硬めの敵」を新たな種類として追加したいということですね。以下の流れで追加できます。
✅ ゴール
A(自機追尾型)とは別に、「直線移動で少し硬い敵(例:Bタイプ)」をPrefabとして追加し、自動で出現できるようにする
🎯 手順概要
ステップ | 内容 |
---|---|
① | 新しい敵のスクリプトを作る(または継承して作る) |
② | 新しいPrefabを作る(Aを複製して編集) |
③ | スポナー(EnemySpawner)で新しい敵を生成するよう追加 |
🧩 Step ①:直線移動する敵用のスクリプトを作成
以下は「右から左に直線で移動する敵」の例です:
using UnityEngine;
public class StraightEnemy : MonoBehaviour
{
public float speed = 5f; // Aより速く
public int maxHP = 5; // 少し硬い
private int currentHP;
private Rigidbody2D rb;
void Start()
{
currentHP = maxHP;
rb = GetComponent<Rigidbody2D>();
rb.gravityScale = 0;
rb.isKinematic = true;
}
void FixedUpdate()
{
// 左方向へ直進(右→左)
Vector2 movement = Vector2.left * speed * Time.fixedDeltaTime;
rb.MovePosition(rb.position + movement);
}
public void TakeDamage(int damage)
{
currentHP -= damage;
if (currentHP <= 0)
{
Destroy(gameObject);
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Bullet"))
{
TakeDamage(1);
Destroy(collision.gameObject);
}
else if (collision.CompareTag("Player"))
{
PlayerController pc = collision.GetComponent<PlayerController>();
if (pc != null)
{
pc.TakeDamage(1);
}
Destroy(gameObject);
}
}
}
🧩 Step ②:Prefab を複製して「直線敵」にする
・Enemy_A(自機追尾タイプ)のプレハブを選択して Ctrl + D(複製)
・名前を Enemy_B に変更(例)
・スクリプトを Enemy.cs → StraightEnemy.cs に差し替える
・必要に応じて Sprite や色、サイズ、HP 値などを変更
🧩 Step ③:スポナーに直線敵を追加(ランダム生成 or 固定でもOK)
方法①:どちらかをランダムに出す
public GameObject[] enemyPrefabs; // AとBを登録
void SpawnEnemy()
{
int index = Random.Range(0, enemyPrefabs.Length);
int spawnIndex = Random.Range(0, spawnPoints.Length);
Instantiate(enemyPrefabs[index], spawnPoints[spawnIndex].position, Quaternion.identity);
}
Inspector で Enemy_A と Enemy_B を enemyPrefabs[0] と [1] に登録する
方法②:一定確率でBを出す(例:Bは20%)
void SpawnEnemy()
{
GameObject prefabToSpawn = Random.value < 0.2f ? enemyPrefabB : enemyPrefabA;
int spawnIndex = Random.Range(0, spawnPoints.Length);
Instantiate(prefabToSpawn, spawnPoints[spawnIndex].position, Quaternion.identity);
}
✅ まとめ
項目 | 設定例(Enemy_B) |
---|---|
速度 | speed = 5(追尾型より速い) |
HP | maxHP = 5(少し硬い) |
移動 | Vector2.left(直線移動) |
プレハブ | Aを複製+スクリプト差し替え |
スクリプトを少し変えてSpriteで画像を差し替えました。
Quaternion.identityが復活しているのでこれも変更して左を向くようにします。
ここでエラーが出てきました。
エラーが出ました
ChatGPTの回答(クリックで展開)
❌ エラー内容(CS0103)
Assets\Script\Enemy\EnemySpawner.cs(26,21): error CS0103: The name 'enemyPrefabs' does not exist in the current context
これは:
変数 enemyPrefabs が宣言されていないのに使われている
という意味です。
✅ 解決方法:enemyPrefabs を宣言する
EnemySpawner.cs のクラス内に、以下の1行を追加してください:
public GameObject[] enemyPrefabs; // A型とB型の敵Prefabを入れる配列
よく見るとenemyPrefabとenemyprefabsが混在していたようです。
さらにエラーが出てきました。
ChatGPTの回答(クリックで展開)
'GameObject' に 'Length' の定義が含まれておらず…
CS1061
は、enemyPrefabs
を配列ではなく単なる GameObjectとして宣言してしまっていることが原因です。
✅ 問題のコード例(エラーになる)
public GameObject enemyPrefabs; // ❌ ← 配列じゃない!
このままでは enemyPrefabs.Length
を呼ぶとエラーになります。
(GameObject は .Length を持っていません)
✅ 正しいコード(修正)
public GameObject[] enemyPrefabs; // ✅ ← 配列にする!
手書きでenemyPrefabsにしたところその前のGameObject[]とするのを見逃していたようです。
[].lengthで配列の要素数を返すはずが、GameObjectが配列として認識されていなかったため、エラーが発生したようです。配列を使う時は気を付けます。
EnemyPrefabsにEnemy2種類を配置
ゲームを動かしてみると更なるエラーが…
は、既にDestroyされた(削除された)オブジェクトをInstantiateしようとしたときに発生します。
ChatGPTの回答(クリックで展開)
Instantiate(enemyPrefabs[index], ...)
を呼び出そうとしたとき、その
enemyPrefabs[index]
が null または削除された状態 になっている。
✅ 原因と対策まとめ
原因 | 対策 |
---|---|
enemyPrefabs[n] に設定されていたプレハブを Projectビューから削除した | Unityエディタで EnemySpawner を選び、Inspectorの enemyPrefabs[] を確認。nullや「Missing」があれば再設定 |
ゲーム開始前はアタッチされているものが開始後に1度消えるとそのままnoneになり消えてしまう状態でした。考えたら分かったのですがHierachyに存在しているObjectをアタッチしたことが原因だったようです。
Projectに保存したPrefabを設定すれば問題ないことが分かりました。
なんとなく彩り(?)が加わった気がします。
この敵が邪魔してくる中アイテムを取得し、自機を強化。最終的に出現するボスを時間内に倒す、というところまでをゲーム内容として考えています。
まだ、アイテムの実装、ボスの調整などやらなければならないことがたくさんですね。しかもちょっとずつ難易度が上がっていきそうです。
とりあえず今回はここまでにします。お疲れさまでした。
◇ 関連記事
敵の実装はこちら