Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

SOLVED Need help to add delay in an instantiate for loop

Discussion in 'Scripting' started by padouvalente, Apr 24, 2020.

  1. padouvalente

    padouvalente

    Joined:
    Apr 24, 2020
    Posts:
    4
    Hello, i am pretty new in unity coding with C#.

    I try to code a spawn manager with an index of different prefabs that make random wave with a for loop making more and more enemies each new wave.

    It's working well dispite one thing : enemies often spawn at the same position and that make them collide and glitch and i would like to make them spawn (be instantiate) not all at the same time during a wave, make them spawn one by one after a short delay (0.5seconds for exemple). The goal is to have wave of 50 enemies, so its important to don't make them spawn at the same time.

    I tried use IEnumerator and Coroutine with wait for seconds in many ways in the code but it never worked properly (no enemies spawns or i get inifinity of enemies)

    How do i have to use WaitForSeconds properly for this code or maybe there is another way to do it ?

    PS : ObjectsOfType<MoveMiddle> is a script attached to all enemies that i use to count enemies in this script.

    thank you in advance

    Code (CSharp):
    1.    
    2.     using System.Collections;
    3.     using System.Collections.Generic;
    4.     using UnityEngine;
    5.    
    6.     public class SpawnManagerXaxisTest : MonoBehaviour
    7.     {
    8.         public GameObject[] enemyPrefabs;
    9.         public float spawnRangeZ = 20;
    10.         public float spawnPosX = 20;
    11.         public int enemyCount;
    12.         public int waveNumber = 1;
    13.  
    14.         private void Start()
    15.         {
    16.             SpawnRandomEnemy(waveNumber);
    17.         }
    18.  
    19.         void SpawnRandomEnemy(int enemiesToSpawn)
    20.         {
    21.             int enemyIndex = Random.Range(0, enemyPrefabs.Length);
    22.             for (int i = 0; i < enemiesToSpawn; i++)
    23.             {
    24.                 Instantiate(enemyPrefabs[enemyIndex], GenerateSpawnPosition(),
    25.                 enemyPrefabs[enemyIndex].transform.rotation);
    26.             }
    27.         }
    28.    
    29.         void Update()
    30.         {
    31.             enemyCount = FindObjectsOfType<MoveMiddle>().Length;
    32.             if (enemyCount == 0)
    33.             {
    34.                 waveNumber++;
    35.                 SpawnRandomEnemy((waveNumber));
    36.             }
    37.         }
    38.    
    39.         private Vector3 GenerateSpawnPosition()
    40.         {
    41.             Vector3 spawnPos = new Vector3(spawnPosX, 1, Random.Range(-spawnRangeZ, spawnRangeZ));
    42.             return spawnPos;
    43.         }  
    44.     }
    45.  
     
  2. sbalanoff

    sbalanoff

    Joined:
    Nov 14, 2019
    Posts:
    36
  3. padouvalente

    padouvalente

    Joined:
    Apr 24, 2020
    Posts:
    4
    thank you for repling @joebalanoff

    I was using InvokeRepeating in the first version of the script but that don't allow me to make waves with more and more enemies

    For example, if i use :
    Code (CSharp):
    1. InvokeRepeating("SpawnRandomEnemy", startDelay, spawnInterval);
    i can't add the int value waveNumber for increasing the number of enemy at each wave.

    Because i can't use InvokeRepeating like this : InvokeRepeating("SpawnRandomEnemy((waveNumber))"

    Maybe i don't understand the way to use InvokeRepeating properly to do it.
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,871
    Please don't use InvokeRepeating... it's unwieldy and error prone (no compile time checking, no good way to stop it at the right time). Just use a coroutine:
    Code (CSharp):
    1.     // Number of enemies to spawn
    2.     public int WaveNumber = 1;
    3.     // Number of seconds between each spawn
    4.     public float SpawnDelay = .1f;
    5.     var random = new System.Random();
    6.  
    7.     void Start() {
    8.         StartCoroutine(SpawnEnemies(NumberOfEnemies, SpawnDelay));
    9.     }
    10.  
    11.     IEnumerator SpawnEnemies(int count, float delay) {
    12.         for (int i = 0; i < count; i++) {
    13.             int enemyIndex = random.Next(enemyPrefabs.Length);
    14.             var prefab = enemyPrefabs[enemyIndex];
    15.             Instantiate(prefab, GenerateSpawnPosition(), prefab.transorm.rotation);
    16.             yield return new WaitForSeconds(delay);
    17.         }
    18.     }
    19.  
    20.     void Update() {
    21.        if (/* all enemies are dead*/) {
    22.          StartCoroutine(SpawnEnemies(++WaveNumber, SpawnDelay));
    23.        }
    24.     }
    25.    
    Also note that I replaced Unity's Random.Range (which deals with floats and also has a possibility of picking the number at the top of your range (which would give you an array out of bounds exception), with System.Random.Next() which is more what we want for array indexing.
     
    Last edited: Apr 24, 2020
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,764
    This is actually incorrect. Take another look at Random.Range's documentation, specifically the second override. The float version of Range is inclusive on both ends, but if you pass in two int's, it's exclusive on the upper end, for exactly this reason.
     
    matkoniecz and PraetorBlue like this.
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,871
    Well I had no idea, thanks! But that difference in behavior in the two overrides makes me want to use it even less o_O.
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,764
    TBH, with the infinitesimal odds that a randomly generated float would actually equal either end of the range make it entirely academic whether that variant is inclusive or exclusive.
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,871
    Hey who knows - what if this guy's game gets super popular? 1 in 4 billion enemy spawns could equate to a couple hundred crashes a month.
     
  9. padouvalente

    padouvalente

    Joined:
    Apr 24, 2020
    Posts:
    4
    i finally use your advices to make this script :

    Code (CSharp):
    1. public class SpawnManagerXaxisTest2 : MonoBehaviour
    2. {
    3.     public GameObject[] enemyPrefabs;
    4.     public float spawnRangeZ = 20;
    5.     public float spawnPosX = 20;
    6.     public float SpawnDelay = 0.5f;
    7.  
    8.     public int enemyCount;
    9.     public int waveNumber = 1;
    10.  
    11.     void Start()
    12.     {
    13.         StartCoroutine(SpawnEnemies(waveNumber, SpawnDelay));
    14.     }
    15.  
    16.     IEnumerator SpawnEnemies(int count, float delay)
    17.     {
    18.         for (int i = 0; i < count; i++)
    19.         {
    20.             int enemyIndex = Random.Range(0, enemyPrefabs.Length);
    21.             var prefab = enemyPrefabs[enemyIndex];
    22.             Instantiate(prefab, GenerateSpawnPosition(), prefab.transform.rotation);
    23.             yield return new WaitForSeconds(delay);
    24.         }
    25.     }
    26.  
    27.     void Update()
    28.     {
    29.         enemyCount = FindObjectsOfType<MoveMiddle>().Length;
    30.         if (enemyCount == 0)
    31.         {
    32.             waveNumber++;
    33.             StartCoroutine(SpawnEnemies(++waveNumber, SpawnDelay));
    34.         }
    35.     }
    36.         private Vector3 GenerateSpawnPosition()
    37.     {
    38.         Vector3 spawnPos = new Vector3(spawnPosX, 1, Random.Range(-spawnRangeZ, spawnRangeZ));
    39.         return spawnPos;
    40.     }
    41.  
    42. }
    That's seems working perfectly, thank you @PraetorBlue . I was not using IEnumerator properly, now i am ++level in coding

    I don't understand the taking and ending about Unity's Random.Range so i do it by the way i know, maybe i will understand one day.

    Thank you again !
     
  10. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,871
    Hey awesome!

    Just so you know I think there's a bug in your new code:
    Code (CSharp):
    1.             waveNumber++;
    2.             StartCoroutine(SpawnEnemies(++waveNumber, SpawnDelay));
    This will increment waveNumber twice, so you will start with wave 1, then get wave 3, etc..
    You probably want to get rid of one of those increment operations!
     
    matkoniecz likes this.
  11. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Just a quick note:
    Be very careful about starting coroutines in Update. If you end up accidentally creating a condition that's true every frame, you'll be starting multiple different coroutines, each one running their own separate logic.
     
    PraetorBlue likes this.
  12. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,764
    Along these lines, if your player kills the first enemy before the second one spawns, the next wave will begin immediately (both waves will be spawning simultaneously).

    Also if you have an error that prevents the object from spawning correctly you'll have probably an infinite loop of spawns.
     
    PraetorBlue likes this.
  13. padouvalente

    padouvalente

    Joined:
    Apr 24, 2020
    Posts:
    4
    Thanks but I know that, i made the enemies non reachables by the player untill another one spawn
    The player movements are limited to a zone and the enemies spawn further and i hope to have no infinit loop
    I am learning coding since few days, i have no idea how to code it more properly, i prefer to try just make it work for now and do the rest of the gameplay of the game.
     
  14. DoofusKing

    DoofusKing

    Joined:
    Jun 23, 2023
    Posts:
    1
    This was very helpful for me because I had a similar issue. Thanks PraetorBlue.