Squiddu
Squiddu

Reputation: 29

Enemy spawner makes Unity freeze on play

I'm trying to make an enemy spawner for my game, but whenever I try playing the game with the enemy spawning script active, it freezes Unity.

The script is supposed to essentially only allow enemies to spawn for certain amounts of time. I assume there's some infinite loop, but I can't tell (I'm still fairly inexperienced with C#).

What's exactly wrong with this code?

EnemySpawner.cs:

{
    public GameObject enemyPrefab;
    public Transform spawner;
    public float spawnRate = 1f;
    public float nextSpawn;
    public float nextSpawnAllow = 3f;
    public float spawnAllowRate;
    public float timer = 2f;
    GameObject player;
    bool ShouldSpawnEnemy = false;
    
    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.Find("Player Ship");
    }

    public void SpawnCheck()
    {
        while (!ShouldSpawnEnemy)
        {
            nextSpawnAllow -= Time.deltaTime;

        }
        
            
        ShouldSpawnEnemy = true;
        timer -= Time.deltaTime;

        do 
        {
            ShouldSpawn();
        }
        while (timer !<= 0);

        timer = spawnAllowRate/1;
        nextSpawnAllow = spawnAllowRate/1;
        ShouldSpawnEnemy = false;
    }

    public void ShouldSpawn()
    {
        if(player != null)
        {
            if(ShouldSpawnEnemy == true)
            {
                nextSpawn -= Time.deltaTime;
                if(nextSpawn <= 0)
                {
                    nextSpawn = spawnRate/1;
                    
                    GameObject enemy = Instantiate(enemyPrefab, spawner.position, spawner.rotation);
                }
            }

        }
    }




    // Update is called once per frame
    void Update()
    {   
        SpawnCheck();            
    }

    
}

Unity 2020.3.4f1

Upvotes: 0

Views: 178

Answers (1)

TEEBQNE
TEEBQNE

Reputation: 6275

Your issue is you have an infinite loop. In your Update() function which runs every frame, you are calling your function SpawnCheck(). Inside of this function you have the lines

public void SpawnCheck()
    {
        while (!ShouldSpawnEnemy)
        {
            nextSpawnAllow -= Time.deltaTime;

        }
...

As your boolean ShouldSpawnEnemy starts as false, it will run this while loop indefinitely. I would recommend using an IEnumerator with a StartCoroutine instead of an Update loop. If you need an example using these functions I can post an example snippet.

Edit: Here is how I would approach your situation by using IEnumerators. I am not sure this is 100% what you want, but this is a direction you can take. Tweak what you need.

using System.Generic;   // needed for the IEnumerator

public GameObject enemyPrefab;
public Transform spawner;
public float spawnRate = 1f;
public float nextSpawnAllow = 3f;
public float timer = 2f;
GameObject player;

private void Start()
{
    player = GameObject.Find("Player Ship");
    StartCoroutine(AttemptToSpawnEnemies());
}

public IEnumerator AttemptToSpawnEnemies()
{
    // IEnumerators to explain very simpily (not techincally correct) run parallel to your code
    // it is NOT multithreading, but will run snippets up until a yield and will jump back in the next frame
    
    // think of them almost like an Update method that does a little work every frame
    
    // as they are functions that do not repeat but run where they left off, we can have local variables
    float currentTime = 0.0f;
    
    // keep incrementing our timer until it exceeds when we want to spawn next 
    while(currentTime <= nextSpawnAllow)
    {
        currentTime += Time.deltaTime;
        
        // we use a yield return to stop the progress here until the condition in the while() is met
        yield return null;
    }
    
    // our spawn increment period is met, now lets start spawning enemies
    // you can do this by starting a new coroutine inside of this one
    yield return StartCoroutine(SpawnEnemies());
    
    // all of our enemies are now spawned, so start the loop again to keep spawning!
    StartCoroutine(AttemptToSpawnEnemies());
}

public IEnumerator SpawnEnemies()
{
    // how long we have been spawning enemies
    float timeToSpawnEnemies = 0.0f;
    
    // how long it has been since we last spawned an individual enemy
    float currentEnemyTimer = spawnRate;
    
    if(player != null)
    {
        // until our enemy timer exceeds the timer that allows us to spawn enemies, keep trying to spawn enemies
        while(timeToSpawnEnemies <= timer)
        {
            timeToSpawnEnemies += Time.deltaTime;
            currentEnemyTimer += Time.deltaTime;
            
            // time to spawn an enemy as the timer is now off cooldown
            if(currentEnemyTimer >= spawnRate)
            {
                // spawn our enemy - you only need to grab the reference if you need to edit the object you are spawning
                Instantiate(enemyPrefab, spawner.position, spawner.rotation)
                
                // reset the timer
                currentEnemyTimer = 0.0f;
            }
            
            // remember to yield or face another infinite loop
            yield return null;
        }
    }
    yield return null;
}

Let me know if you have more questions or portions of the code do not work as expected. It is currently untested and is just used as means to help teach Coroutines / IEnumerators.

Upvotes: 2

Related Questions