Spawn enemies so they aren't instantiated on top of each other (C#)
Hello my fellow great Unites! I have an enemy spawn system that spawns out enemies using Ienumerator inside a set of borders. Now the only problem is that the enemies will sometimes spawn out on the player, or another object...
How can I fix this problem? Using a for loop?
Here's the code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemySpawn : MonoBehaviour
{
// EnemyPrefabs
public GameObject EnemyBouncingPrefab;
public GameObject EnemyGrowerPrefab;
public GameObject EnemyMinePrefab;
//Show where spawn is prefab
public GameObject showEnemySpawn;
public GameObject showEnemyFlyBySpawn;
public GameObject showEnemyGrowerSpawn;
public GameObject showEnemyMineSpawn;
// Array of spawn points
public Transform[] spawnPointsEnemyFollow;
public Transform[] spawnPointsEnemyExplode;
public Transform[] spawnPointsEnemyFlyBy;
// Borders
public Transform borderTop;
public Transform borderBottom;
public Transform borderLeft;
public Transform borderRight;
//Used to put active enemies in a list
public static List<GameObject> activeEnemies;
void Start ()
{
// Starts a coroutine function for spawning bouncing enemies and fly by enemy
StartCoroutine(SpawnBouncingEnemy());
StartCoroutine(SpawnGrowerEnemy());
StartCoroutine(SpawnEnemyFlyBy());
StartCoroutine(SpawnMineEnemy());
}
void Awake()
{
// Create the list
activeEnemies = new List<GameObject>();
}
// Spawn a Bouncing enemy
IEnumerator SpawnBouncingEnemy()
{
//Wait 3 to 5 seconds when game starts to spawn a ball
yield return new WaitForSeconds(Random.Range(1, 3));
while(true)
{
//Calls the function to set random position
Vector2 spawnPoint = RandomPointWithinBorders();
// Show spawn location for one second
Object marker = Instantiate(showEnemySpawn, spawnPoint, Quaternion.identity);
yield return new WaitForSeconds(1);
Destroy(marker);
// Spawn enemy
GameObject newEnemy = (GameObject) Instantiate(EnemyBouncingPrefab, spawnPoint, Quaternion.identity);
activeEnemies.Add(newEnemy);
yield return new WaitForSeconds(Random.Range(4, 6));
}
}
// Spawn a Grower enemy
IEnumerator SpawnGrowerEnemy()
{
//Wait 3 to 5 seconds when game starts to spawn a "grower"
yield return new WaitForSeconds(Random.Range(20, 40));
while(true)
{
//Calls the function to set random position
Vector2 spawnPoint = RandomPointWithinBorders();
// Show spawn location for one second
Object marker = Instantiate(showEnemyGrowerSpawn, spawnPoint, Quaternion.identity);
yield return new WaitForSeconds(1);
Destroy(marker);
// Spawn enemy
GameObject newEnemy = (GameObject) Instantiate(EnemyGrowerPrefab, spawnPoint, Quaternion.identity);
activeEnemies.Add(newEnemy);
yield return new WaitForSeconds(Random.Range(20, 50));
}
}
// Spawn a Mine enemy
IEnumerator SpawnMineEnemy()
{
//Wait 40 to 60 seconds when game starts to spawn a "Mine_enemy"
yield return new WaitForSeconds(Random.Range(45, 60));
while(true)
{
//Calls the function to set random position
Vector2 spawnPoint = RandomPointWithinBorders();
// Show spawn location for one second
Object marker = Instantiate(showEnemyMineSpawn, spawnPoint, Quaternion.identity);
yield return new WaitForSeconds(2);
Destroy(marker);
// Spawn enemy
GameObject newEnemy = (GameObject) Instantiate(EnemyMinePrefab, spawnPoint, Quaternion.identity);
activeEnemies.Add(newEnemy);
yield return new WaitForSeconds(Random.Range(50, 60));
}
}
public Vector2 RandomPointWithinBorders()
{
//Code that will spawn a bouncing ball and ShowSpawn at a random position inside the borders
Vector2 random = new Vector2();
random.x = (int)Random.Range(borderLeft.position.x, borderRight.position.x);
random.y = (int)Random.Range(borderBottom.position.y, borderTop.position.y);
return random;
}
}
Answer by Nimred · Apr 27, 2016 at 10:09 AM
The usual way to solve this problem is to set up multiple spawn points in your level. You place them manually in the editor, mark them in a special way (a tag, or more likely you write up a "SpawnPoint" component), then when spawning your objects you keep track of which spawn point has been used, and spawn the object at an unused spawn point's location.
In order to reuse the spawn points, you could also give them a simple box collider, set up as a trigger. This way once you spawn an object at this location, the spawned object enters the trigger; once the spawned object exits the trigger, you know you can use the spawn point again.
You might also want to have an enum value on the SpawnPoint component to decide if it's a player spawn point, an AI spawn point, etc...
Answer by meat5000 · Apr 27, 2016 at 10:09 AM
Use Physics.OverlapSphere to check the space before Instantiate.
I've seen this solution before but I'm not quite sure where to implement it
You implement it before the Instantiate at spawnpoint. So if there's nothing in the collider[]
you fire the Instantiate. You will probably need a layermask to select the layers you want to use in your detection.