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

Question How could I instantiate several objects in a row?

Discussion in 'Scripting' started by jleven22, Dec 7, 2022.

  1. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    421
    I'm wanting to instantiate a gameobject upon enemy death several times, every .2f seconds.

    I'm getting screwed up figuring out whether to do a for loop or a coroutine. What would be the best way to pull this off?

    For the visual, essentially it's like a bomb chain effect so I'll want to instantiate an object X times in random positions.

    Thanks!
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,612
    Well you just yield a delay in a co-routine loop. It can be any kind of loop. Like this:

    Code (CSharp):
    1. public IEnumerator BombChain(int bombCount, float delay)
    2. {
    3.     for (int i = 0; i < bombCount; i++)
    4.     {
    5.        //spawn bombs, etc
    6.         yield return new WaitForSeconds(delay);
    7.     }
    8. }
     
    Bunny83 and SeerSucker69 like this.
  3. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    A for loop will instantiate everything at the same time. So all 3 objects unless you iterate once per frame. But then you may aswell have just thrown your death position into a list of a manager script and handled what to do about instantiating when that list has content and it’s elements are being checked.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,612
    It's a co-routine with a delay between loops, so... no it won't?
     
  5. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    did I say something wrong?
     
  6. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    421
    So I ended up doing this (on a manager script because I destroy the enemy after running the bomb chain ienumerator).

    Here's what I ended up writing:

    Code (CSharp):
    1.     private void randomizeBombPositionInts()
    2.     {
    3.         randomX = Random.Range(-1.5f, 1.5f);
    4.         randomY = Random.Range(-1.5f, 1.5f);
    5.     }
    6.  
    7.  
    8.     public IEnumerator BombChain(Vector3 enemyPosition)
    9.     {
    10.  
    11.         for (int i = 0; i < magicBomb; i++)
    12.         {
    13.             Debug.Log("Bomb spawned");
    14.             randomizeBombPositionInts();
    15.  
    16.             Instantiate(magicBombItem, new Vector2(enemyPosition.x + randomX, enemyPosition.y + randomY), transform.rotation);
    17.             yield return new WaitForSeconds(.1f);
    18.         }
    19.     }
    The issue I'm encountering is that it's only running the for loop once. I have debugged to confirm this, and I can also confirm that the "magicBomb" int is 3 when running, so it should be dropping 3 bombs right?
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,612
    What object is actually starting this co-routine? Depending on how you've done it, the object you're destroying could still be the monobehaviour this co-routine is attached to; thus it will only perform one execution before being destroyed and killing the co-routine.

    An example of how's being called would be good.
     
  8. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    421
    Sure yeah, so the script above is on a manager called ItemTracker and the coroutine is being called from an enemy before it dies. That's here:

    Code (CSharp):
    1.             if (ItemTracker.instance.magicBomb > 0)
    2.             {
    3.                 StartCoroutine(ItemTracker.instance.BombChain(transform.position));
    4.             }
    I understand your concern about the enemy being destroyed, but as far as I'm aware all I'm doing is starting the coroutine and setting the position of the bomb spawn from the enemy before destroying it. Everything else should be playing out on the ItemTracker without issue.

    Does that make sense?
     
  9. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,612
    No you're doing exactly what I said would cause the issue. You're starting the co-routine from the object being destroyed, tying the lifetime of the co-routine to it. Thus, when the enemy is destroyed, the co-routine is killed.

    Instead, do this:
    Code (CSharp):
    1. if (ItemTracker.instance.magicBomb > 0)
    2. {
    3.     ItemTracker instance = ItemTracker.instance;
    4.     instance.StartCoroutine(instance.BombChain(transform.position));
    5. }
    Or, give your manager a static method that starts up the co-routine itself, rather than the enemy being in charge of this logic themselves.
     
    Last edited: Dec 8, 2022
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,522
    You can avoid all this ownership and lifetime noise by using a simple "do something later" script.

    I use my CallAfterDelay class for delayed action.

    https://gist.github.com/kurtdekker/0da9a9721c15bd3af1d2ced0a367e24e

    See usage notes at bottom below gist code.

    In your case you would issue CallAfterDelay instances for each of the bombs with ever-increasing delays, then you can just go away fearlessly and as each CallAfterDelay matures, the spawn will happen, something like:

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class MakeLineOfBombs : MonoBehaviour
    4. {
    5.     void Start()
    6.     {
    7.         for (int i = 0; i < 10; i++)
    8.         {
    9.             Vector3 spawnPosition = Vector3.right * (i - 5);
    10.  
    11.             float time = i * 0.25f;
    12.  
    13.             // @kurtdekker... get this from:
    14.             // https://gist.github.com/kurtdekker/0da9a9721c15bd3af1d2ced0a367e24e
    15.             CallAfterDelay.Create( time, () => {
    16.                 var bomb = GameObject.CreatePrimitive( PrimitiveType.Sphere);
    17.                 bomb.transform.position = spawnPosition;
    18.             });
    19.         }
    20.  
    21.         // we can happily go away now
    22.         Destroy(gameObject);
    23.     }
    24. }
     
    Last edited: Dec 9, 2022
  11. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    421
    So this wouldn't work for some reason - the ItemTracker is already an instance of itself, so should be good there.
     
  12. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    421
    Thank you! I ended up doing a variation of this that worked.

    I created a "BombChainCreator" script & prefab, and just had that spawn at each enemy's death, then from there instantiate the bombs from that script in the position of that new frefab object. It's working great now!
     
    Kurt-Dekker likes this.