I'm working on my second fps game.
I know that this question maybe repeated but I couldn't find any fix to my problem. Because everything seems to be fine in my script.
So,
I've a scene in which I've an empty gameobject(spawnPoints). it contains all the spawnpoint for spawning enemy. PURPOSE and PROBLEM I don't want to overlap the enemies. There are total 6 spawn points. If I put 5 as the size of spawn point. Everything works fine...i.e, enemies don't overlap. But if I put 7, 8 or 9 as the size of spawnpoint array. enemies overlaps. I'm using Physics.overlapsphere for detection of the colliders.
I don't want to spawn the enemies if there is no place to spawn. If there is no place to spawn then I want the enemies to wait...until any spawn point is clear.
CODE
using UnityEngine;
using System.Collections;
public class EnemySpawns :
MonoBehaviour {
public GameObject Enemy;
public Transform[] spawnPoints;
public float spawnTime;
public int maxEnemy = 5;
private int currentEnemy = 0;
bool isSpawn;
public float EnemyRadius = 16f;
void Start(){
isSpawn = false;
InvokeRepeating ("spawnEnemy", spawnTime, 5.4f);
isSpawn = true;
}
public void spawnEnemy(){
for (int i = 0; i < spawnPoints.Length; i++) {
Collider[] cols = Physics.OverlapSphere (spawnPoints [i].transform.position, EnemyRadius);
foreach (Collider hit in cols) {
if (hit.tag == "AI Controller")
{
isSpawn = false;
return;
}
else
{
isSpawn = true;
}
}
}
if (isSpawn == true) {
if (currentEnemy <= maxEnemy) {
Transform currentSpawnPoint = spawnPoints [Random.Range (0, spawnPoints.Length)].transform; //pickrandom
Instantiate (Enemy, currentSpawnPoint.position, currentSpawnPoint.rotation);
currentEnemy++;
}
}
if (currentEnemy == maxEnemy) { //stop spawning enemies
CancelInvoke ();
}}}
you had several mistakes in your logic there, I refactored your code as a coroutine, as I think it's more fitting in your case.
StartCoroutine(SpawnEnemies());
}
public IEnumerator SpawnEnemies()
{
while (_spawnedEnemies < _enemiesToSpawn.OrderBy(o => Random.value))
{
// You should really store these "magic numbers" in variables (5.4f)
yield return new WaitForSeconds(5.4f);
foreach (var spawnPoint in _spawnPoints)
{
var cols = Physics.OverlapSphere(spawnPoint.transform.position, EnemyRadius);
// I would recommend not using tags in general, instead maybe GetComponent<AIController>() or even better, use an enemy layer and use a mask when overlapping the sphere
if (cols.Any(c => c.tag == "AI Controller"))
{
continue;
}
Instantiate(_enemyPrefab, spawnPoint.position, spawnPoint.rotation);
currentEnemy++;
break;
}
}
}
Basically, the function manages it's lifecycle vs. using invoke, it iterates on all spawn points, checks if any AI controllers are there, and if not, spawns an enemy there.
If all spawn points are occupied, the function will wait for the defined delay (5.4s) and then try again.
_enemiesToSpawn.OrderBy(o => Random.value)
is a quick shuffle in low performance points (in your case this will get called N times every 5.4 seconds, which is negligible)
You can also move the yielding after the for loop, that way you will first spawn an enemy with no delay and only then wait for the next spawn.