Search Unity

Question How do I make my coroutine loop itself?

Discussion in 'Scripting' started by truederfor11, Apr 12, 2021.

  1. truederfor11

    truederfor11

    Joined:
    Apr 11, 2021
    Posts:
    23
    Long story short I want to spawn enemies until a max count is reached, and then when enemies are destroyed spawn new ones. Everything is working but the coroutine I use to spawn the enemies ends itself after it reaches the max count for the first time. My question is, how do I make my coroutine loop itself when it's done? tried using while loops in various points and they all just crash unity. Code below.

    Thanks for helping me :)

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemySpawner : MonoBehaviour
    6. {
    7.     public GameObject Enemy;
    8.     public int xPos;
    9.     public int zPos;
    10.     public int EnemyCount;
    11.     public static bool CheckEnemyCount;
    12.     private int MaxCount;
    13.     private float SpawnSpeed;
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.         CheckEnemyCount = false;
    19.         MaxCount = 2;
    20.         SpawnSpeed = 1f;
    21.             StartCoroutine(EnemyDrop());
    22.     }
    23.         IEnumerator EnemyDrop()
    24.         {
    25.            while(EnemyCount < MaxCount)
    26.            {
    27.                 Debug.Log("working");
    28.                 xPos = Random.Range(-4,5);
    29.                 zPos = Random.Range(-12,-6);
    30.                 Instantiate(Enemy, new Vector3(xPos, 2, zPos), Quaternion.identity);
    31.                 yield return new WaitForSeconds(SpawnSpeed);
    32.                 EnemyCount += 1;
    33.            }  
    34.         }
    35.     void Update()
    36.     {
    37.         if (CheckEnemyCount == true)
    38.         {
    39.             EnemyCount -= 1;
    40.             CheckEnemyCount = false;
    41.  
    42.         }
    43.  
    44.     }
    45.  
    46.  
    47. }
    48.  
     
    Last edited: Apr 12, 2021
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    Your Update method seems a bit off, but as that's not your question, I'll focus on that instead.

    Right now, your Coroutine is called from Start, so your coroutine starts and enters your While loop. Because you have no exit condition, this will loop forever currently. The yield within the While loop is important because without it, the loop never "yields" to allow other code to run.

    So, as of right now, your loop is going forever. So, your question is answered.

    Now, if you want it to stop spawning enemies when max is reached, then you can add an if statement with a break to come out of the while loop, but this all depends on exactly what behavior you want. Do you want it to spawn a new enemy when one dies? Do you want it to wait till an amount of time has passed? Do you want it to spawn in rounds?

    If your description just means "replenish as one dies" then just add an if check, if current == max, don't spawn, then if current < max, spawn. Can leave the coroutine running, just make sure it always yields to allow other code to run, else you get stuck in the while loop.

    Also, there is nothing that prevents you from calling StartCoroutine again from another method if for example you want to track enemies alive and then spawn a second round or whatever. But that depends on your use case.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,739
    The above code does not end itself. Something else must be ending it: destroying the gameobject, setting it inactive, etc.

    The loop is kinda weird: wouldn't you want to count up the enemy precisely when you instantiate?? Why wait "Spawn Speed" before you update the counter, because now your enemy count will always be 1 off.
     
    Brathnann likes this.
  4. truederfor11

    truederfor11

    Joined:
    Apr 11, 2021
    Posts:
    23
    The only thing I am doing that could affect that is destroying the enemy after the bullet hits, which I don't think affects that as it affects the enemy counter properly. I tested with some debug.log lines and the coroutine just stops working after it finishes for the first time. The gameobject the script is attached to is an empty gameobject with just that script, which isn't referened anywhere else - so I really don't think it getting destroyed or deactivated is a thing.
    EDIT - nvm I set the loop to while true and forgot, edited the changes in

    and can you elaborate more on the second part? I don't understand what is making it off by 1 exactly..
    and i'm waiting the spawn speed so the enemies don't just spawn right after the previous one was killed, as that would feel unrealistic
     
    Last edited: Apr 12, 2021
  5. truederfor11

    truederfor11

    Joined:
    Apr 11, 2021
    Posts:
    23

    Yeah my update method is weird but I couldn't get it working in another way honestly. Whatever tho, if it works it works.

    also I set the loop to while true for debugging, edited the actual code in rn.

    Using some debug.log lines I saw the coroutine activates once but the loop only works 2 times and stops as I said before. When the while loop meets the condition it stops trying to meet it again, which causes the problem. Got any idea how to fix it? tried to use an if statement inside a while true loop but that just crashes me :/
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,739
    Why not loop infinitely, and when there aren't enough, spawn?

    Code (csharp):
    1. while(true)
    2. {
    3.   yield return new WaitForSeconds( SpawnSpeed);
    4.  
    5.   if (enemyCount < MaxCount)
    6.   {
    7.     /// do instantiation noise here
    8.     enemyCount++
    9.   }
    10. }
     
    archburger likes this.
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    Pretty much what @Kurt-Dekker showed. It's important in a coroutine if you don't want it to create an infinite loop it must always hit a yield statement inside a loop that has no natural exit. (calling break, or having a conditional exit).
     
  8. truederfor11

    truederfor11

    Joined:
    Apr 11, 2021
    Posts:
    23
    That worked, thanks a lot!
     
    Kurt-Dekker likes this.