Creating Spawn points for zombies

I am trying to make spawnpoints where the zombies spawn and keep spawning like a wave system I looked up a basic way to spawn enemies and I’m running into some issues.

  1. Right now I am only able to spawn zombies once and when they are dead no more spawn

  2. The way the code is set up it makes its very hard to implement for eg. I have to mark the range of x, y and z positions soo zombies can only spawn in that particular area.

  3. As my level has elevations because of how I have set up my terrain if the Y values are not set perfectly the zombies spawn in and either have feet buried in the ground or floating in the air

Is there any more efficient way to make this system which works more dynamically and predictably?

EnemySpawner C#

public class EnemySpawner : MonoBehaviour
{
    [SerializeField] GameObject enemy;
    public int xPos;
    public int yPos;
    public int zPos;
    [SerializeField] float spawnDelay= 01f;
    private int enemyCount;

    void Start()
    { 
       
    }

    private void Update()
    {
        StartCoroutine(SpawnEnemy(spawnDelay, enemy));
    }

    IEnumerator SpawnEnemy(float spawnDelay, GameObject enemy)
    {
        
        while (enemyCount < 20) 
        {
            xPos = Random.Range(80, 74);
            yPos = Random.Range(50, 51);
            zPos = Random.Range(80, 95);
            GameObject newenemy = Instantiate(enemy, new Vector3 (xPos, yPos, zPos), Quaternion.identity);
            yield return new WaitForSeconds(spawnDelay);
            enemyCount += 1;
        }
        
    }
}

@bixarrio and @Nina please save me

Hi Imadity,

What exactly is your idea? How are the zombies supposed to spawn? What are your rules?

From what I understood, you managed to spawn the first wave but not additional zombies. Given I understood that correctly, you could parent the zombies to game objects and check the childCount of those game objects in the Update method or in the SpawnEnemy method. Depending on the childCount, you could spawn new zombies. No need to use the enemyCount variable and take care of dead zombies.

Do not call StartCoroutine in Update. This starts a new coroutine each frame. With a framerate of 60, 60 new coroutines would get started per second. That’s a waste of resources and decreases the performance of your game significantly. Have the coroutine run the entire time or drop the coroutine and write a timer for your Update method.

One more thing you could do to improve the performance of your code: If you use a coroutine and if the value of spawnDelay does not change, cache the WaitForSeconds object instead of creating a new one in each iteration step of the while-loop.

I hope this helped. :slight_smile:

I tried making a new script new a new system where I’m using enums for Spawning Waiting and Counting. Im updating the coroutine to cycle wave after wave. But I got stuck at another place where my script is suppose to count down from 5 secs and as it hits 0 the wave should start spawning but on clicking play the count doest go down on its own and I have to manually set it to 0 for it to start work. But after that it loops through all the states and counts down to negative value which it shouldn’t be doing.

HERES THE CODE AND A VIDEO OF ITS BEHAVIOR = https://youtu.be/gAh34mrXhmo

public class WaveSpawner : MonoBehaviour
{
    public enum SpawnState {SPAWNING, WAITING, COUNTING }

    [System.Serializable]
    public class Wave
    {
        public string name;
        public Transform enemy;
        public int count;
        public float rate;
    }

    public Wave[] waves;

    public int nextWave = 0;
    public float timeBetweenWaves = 5f;
    public float waveCountDown = 0f;

    private float enemySearchTime = 1f;

    private SpawnState state = SpawnState.COUNTING;

     void Start()
    {
        waveCountDown= timeBetweenWaves;   
    }

    void Update()
    {
        if (state == SpawnState.WAITING)
        {
            if (!EnemyIsAlive())
            {
                //next wave
                Debug.Log("Wave completed");
            }
            else {return;}
        }

        if (waveCountDown <= 0)
        {
            if (state != SpawnState.SPAWNING)
            {
                StartCoroutine(SpawnWave(waves[nextWave]));
            }
            else
            {
                waveCountDown -= Time.deltaTime;
            }
        }
    }

    bool EnemyIsAlive()
    {
        enemySearchTime -= Time.deltaTime;
        
        if (enemySearchTime <= 0f)
        {
            enemySearchTime = 1f;
            if (GameObject.FindGameObjectWithTag("Enemy") == null)
            {
                return false;
            }
        }
       return true;
    }

    IEnumerator SpawnWave(Wave _wave)
    {
        Debug.Log("Spawning Wave" + _wave.name);
        state = SpawnState.SPAWNING;

        for (int i = 0; i < _wave.count; i++)
        {
            SpawnEnemy(_wave.enemy);
            yield return new WaitForSeconds(1f / _wave.rate);
        }
        

        state = SpawnState.WAITING;
        yield break; 
    }

    void SpawnEnemy(Transform _enemy)
    {
        Instantiate(_enemy, new Vector3(0, 0, 0), Quaternion.identity);
        Debug.Log("Spawing enemy" + _enemy.name);
    }
}

Your question is why waveCountDown does not get set to 0 or an initial value? That’s because the value gets set in Start() but then, only waveCountDown -= Time.deltaTime; gets executed.

After the zombies were spawned (or whatever your idea is), you have to execute waveCountDown= timeBetweenWaves; to reset your timer. If I understood your code correctly, I would expect this line in the SpawnWave method where you set state = SpawnState.WAITING;.

Ohk After a whole day of head scratching I see where I went wrong I made a silly mistake where i forgot to add a “{}” which changed the logic of the method and I didn’t see it as I did not get any errors in my script.

Mistake in Update-

if (waveCountDown <= 0)
       {
           if (state != SpawnState.SPAWNING)
           {
               StartCoroutine(SpawnWave(waves[nextWave]));
           }
       { - // not having this bracket caused problems 
           else
           {
               waveCountDown -= Time.deltaTime;
           }
       }

Ill anyway post there whole code now its working and is finished if someone else wants to implement Zombies or ant enemy spawning in there game

public class WaveSpawner : MonoBehaviour
{ 
	public enum SpawnState { SPAWNING, WAITING, COUNTING };

[System.Serializable]
public class Wave
{
	public string name;
	public Transform enemy;
	public int count;
	public float rate;
}

public Wave[] waves;
private int nextWave = 0;
public int NextWave
{
	get { return nextWave + 1; }
}

public Transform[] spawnPoints;

public float timeBetweenWaves = 5f;
private float waveCountdown;
public float WaveCountdown
{
	get { return waveCountdown; }
}

private float searchCountdown = 1f;

private SpawnState state = SpawnState.COUNTING;
public SpawnState State
{
	get { return state; }
}

void Start()
{
	if (spawnPoints.Length == 0)
	{
		Debug.LogError("No spawn points referenced.");
	}

	waveCountdown = timeBetweenWaves;
}

void Update()
{
	if (state == SpawnState.WAITING)
	{
		if (!EnemyIsAlive())
		{
			WaveCompleted();
		}
		else
		{
			return;
		}
	}

	if (waveCountdown <= 0)
	{
		if (state != SpawnState.SPAWNING)
		{
			StartCoroutine(SpawnWave(waves[nextWave]));
		}
	}
	else
	{
		waveCountdown -= Time.deltaTime;
	}
}

void WaveCompleted()
{
	Debug.Log("Wave Completed!");

	state = SpawnState.COUNTING;
	waveCountdown = timeBetweenWaves;

	if (nextWave + 1 > waves.Length - 1)
	{
		nextWave = 0;
		Debug.Log("ALL WAVES COMPLETE! Looping...");
	}
	else
	{
		nextWave++;
	}
}

bool EnemyIsAlive()
{
	searchCountdown -= Time.deltaTime;
	if (searchCountdown <= 0f)
	{
		searchCountdown = 1f;
		if (GameObject.FindGameObjectWithTag("Enemy") == null)
		{
			return false;
		}
	}
	return true;
}

IEnumerator SpawnWave(Wave _wave)
{
	Debug.Log("Spawning Wave: " + _wave.name);
	state = SpawnState.SPAWNING;

	for (int i = 0; i < _wave.count; i++)
	{
		SpawnEnemy(_wave.enemy);
		yield return new WaitForSeconds(1f / _wave.rate);
	}

	state = SpawnState.WAITING;

	yield break;
}

void SpawnEnemy(Transform _enemy)
{
	Debug.Log("Spawning Enemy: " + _enemy.name);

	Transform _sp = spawnPoints[Random.Range(0, spawnPoints.Length)];
	Instantiate(_enemy, _sp.position, _sp.rotation);
}

}

Good job! :slight_smile:

Here is a little improvement in case you are interested:
WaitForSeconds delay = new WaitForSeconds(1f / _wave.rate);

for (int i = 0; i < _wave.count; i++)
{
    SpawnEnemy(_wave.enemy);
    yield return delay ;
}

Depending on the value of _wave.count, caching the WaitForSeconds object could have a significant positive impact on the performance of your code because the program won’t create x WaitForSeconds objects but reuse the one that has already been created.

1 Like

Thank you soo much for helping out with the issue

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms