Unityで敵をプレイヤーの周りにランダム出現させる|NavMesh対応のSpawnerをコルーチンで作ろう
ゲームでは、敵が少しずつ現れてプレイヤーに迫ってくると、一気に緊張感が出ます。この記事では、その「敵を出すしくみ」を作るスクリプト Spawner を、5つのポイントに分けて読み解いていきます。
この記事で作るもの
次の3つを実現する Spawner スクリプトです。
- プレイヤーの周りのランダムな場所に敵を出す
- 壁の中など、おかしな場所には出さない
- プレイヤーが倒れたら、敵を出すのをやめる
全体のコード
まずは全体を見てみましょう。
using UnityEngine;
using System.Collections;
using UnityEngine.AI;
public class Spawner : MonoBehaviour
{
[SerializeField] private PlayerStatus playerStatus;
[SerializeField] private GameObject enemyPrefab;
private void Start()
{
StartCoroutine(SpawnLoop());
}
private IEnumerator SpawnLoop()
{
while (true)
{
var distanceVector = new Vector3(10, 0);
var spawnPositionFromPlayer = Quaternion.Euler(0, Random.Range(0, 360f), 0) * distanceVector;
var spawnPosition = playerStatus.transform.position + spawnPositionFromPlayer;
NavMeshHit navMeshHit;
if (NavMesh.SamplePosition(spawnPosition, out navMeshHit, 10, NavMesh.AllAreas))
{
Instantiate(enemyPrefab, navMeshHit.position, Quaternion.identity);
}
yield return new WaitForSeconds(10);
if (playerStatus.Life <= 0)
{
break;
}
}
}
}
少し長く見えるかもしれませんが、5つのポイントに分けて読むと、むずかしくありません。
補足:
PlayerStatusは、プレイヤーの体力Lifeなどを持つスクリプトです。ご自身のプロジェクトのプレイヤー用スクリプトに置き換えて試してみてください。
ポイント1:2つの変数
[SerializeField] private PlayerStatus playerStatus;
[SerializeField] private GameObject enemyPrefab;
playerStatusは、プレイヤーの情報です。どこにいるかと体力が残っているかを調べるために使います。enemyPrefabは、出現させる敵の設計図(プレハブ)です。
[SerializeField] がついているので、Unity のインスペクターから、ドラッグ&ドロップで設定できます。

ポイント2:コルーチンで10秒ごとに繰り返す
private void Start()
{
StartCoroutine(SpawnLoop());
}
ゲームが始まると、SpawnLoop() というコルーチンが動き出します。
コルーチンは「途中で待つことができる関数」です。yield return new WaitForSeconds(10); と書くと、そこで10秒間待ってから続きを実行します。この待ち時間のおかげで、約10秒ごとに敵が1体出るようになります。
while (true) は「ずっと繰り返す」という意味ですが、あとで説明する break でやめることができます。

ポイント3:プレイヤーの周りのランダムな場所を決める
ここが、いちばん面白いところです。
var distanceVector = new Vector3(10, 0);
var spawnPositionFromPlayer = Quaternion.Euler(0, Random.Range(0, 360f), 0) * distanceVector;
var spawnPosition = playerStatus.transform.position + spawnPositionFromPlayer;
次の3ステップで考えてみましょう。
- 10m先を指すベクトルを作る。
new Vector3(10, 0)は「横に10進んだ位置」を表します。 - ランダムな角度で回す。
Quaternion.Euler(0, 角度, 0)は、上から見て回す回転です。角度はRandom.Range(0, 360f)で、0〜360度のどれかになります。この回転をベクトルに掛けると、向きだけがランダムに変わります。 - プレイヤーの位置に足す。 プレイヤーの位置に足せば、プレイヤーからちょうど10m離れた円の上の、ランダムな地点になります。
プレイヤーを中心にして、まわりの円周のどこかにサイコロで点を打つようなイメージです。

ポイント4:NavMesh で「出てよい場所」に直す
ただし、ランダムに決めた場所が、壁の中や床のない場所になることもあります。そこで、次のコードを使います。
NavMeshHit navMeshHit;
if (NavMesh.SamplePosition(spawnPosition, out navMeshHit, 10, NavMesh.AllAreas))
{
Instantiate(enemyPrefab, navMeshHit.position, Quaternion.identity);
}
NavMesh.SamplePosition は、「この地点の近くで、歩ける場所(NavMesh の上)はどこ?」と探してくれる関数です。
- 見つかったら
trueが返り、その場所がnavMeshHit.positionに入ります。 - 見つからなかったら
falseなので、この回は敵を出しません。
Instantiate は、プレハブから敵を1体つくる命令です。見つかった場所に出すので、敵はかならず歩ける場所に出現します。

補足:敵が
NavMeshAgentで動くときは、NavMesh の外に出すと動けません。だから、出す前に場所を直しておくことが大切です。
ポイント5:プレイヤーが倒れたら止める
if (playerStatus.Life <= 0)
{
break;
}
10秒待ったあと、プレイヤーの体力(Life)が0以下かどうかを調べます。0以下なら break で while をぬけて、コルーチンが終わります。これでゲームオーバーのあとは、敵が出てこなくなります。

使い方
- シーンに空の GameObject を作り、
Spawnerをアタッチします。 - インスペクターで、
Player Statusにプレイヤーを、Enemy Prefabに敵のプレハブを設定します。 - シーンの床に NavMesh をベイク(作成)しておきます。
- 再生すると、10秒ごとにプレイヤーの近くに敵が出ます。
図解でおさらい
5つのポイントを、図でもう一度たどってみましょう。

まとめ
| やりたいこと | 使うもの |
|---|---|
| 一定時間ごとに繰り返す | コルーチン + WaitForSeconds |
| まわりのランダムな方向を決める | Quaternion.Euler × Vector3 |
| 歩ける場所に直す | NavMesh.SamplePosition |
| プレイヤーが倒れたら止める | Life の確認 + break |
挑戦してみよう
数字を変えるだけで、ゲームの手ざわりはがらりと変わります。ぜひ試してみてください。
- 出現の間隔(10秒)を、5秒や3秒に変えると、ゲームの難しさはどう変わるでしょうか。
- 出現する距離(10m)を変えると、どんな感じになるでしょうか。
- 敵を1回に2体ずつ出すには、どこを変えればよいでしょうか。
自分で数字を変えて、動きを確かめてみましょう。










ディスカッション
コメント一覧
まだ、コメントがありません