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

Instantiate gameobject once before coroutine

Discussion in 'Scripting' started by Lethn, May 16, 2018.

  1. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I'm sure this is going to be typically obvious when I see the code but I'm drawing a blank right now in my head, I have a coroutine that works absolutely fine as is for a trap prefab I have going in my game. However I want to have the iceball instantiate only once as the player enters the collider and then have the coroutine play. The reason for this you might have guessed is because the coroutine always goes first and that means I have to wait x amount of seconds before it spawns even though I want a trap to initially spawn right away.

    Anyone have any ideas? Here's the code I have so far.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class InstantiateIceBallTrap : MonoBehaviour {
    6.  
    7.     private Coroutine _spawnCoroutine = null;
    8.     public GameObject iceBallTrapEmpty;
    9.     public float TrapSpawnTime;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.      
    14.     }
    15.  
    16.     // Update is called once per frame
    17.     void Update () {
    18.  
    19.      
    20.     }
    21.  
    22.  
    23.     void OnTriggerStay2D ( Collider2D other )
    24.  
    25.     {
    26.  
    27.         if (_spawnCoroutine == null)
    28.  
    29.         {
    30.  
    31.             _spawnCoroutine = StartCoroutine ("TrapDelay");
    32.             Debug.Log ("Trap Activated");
    33.         }
    34.     }
    35.  
    36.     void OnTriggerExit2D ( Collider2D other )
    37.  
    38.     {
    39.         if(_spawnCoroutine != null)
    40.         {
    41.             StopCoroutine (_spawnCoroutine);
    42.             _spawnCoroutine = null;
    43.             Debug.Log ("Trap Deactivated");
    44.         }
    45.     }
    46.  
    47.  
    48.     IEnumerator TrapDelay ()
    49.     {
    50.         yield return new WaitForSeconds (TrapSpawnTime);
    51.         Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    52.     }
    53.  
    54.  
    55.  
    56.  
    57.  
    58. }
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    Check if TrapSpawnTime <= 0, if it is, just spawn right away instead of waiting the few seconds.
     
  3. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I think you're misunderstanding what I want, I want that delay to stay, however I want to have the trap instantiate the ice ball once before the coroutine starts so the ice ball spawns in once instantly, then spawns again every couple of seconds, the coroutine itself is fine.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    The coroutine is what is instantiating the 'ice ball'.

    If you want the coroutine to have a delay, but also instantiate before the delay... you need to better define what you mean then.

    What's supposed to happen after the delay?
     
  5. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    to have the iceball only spawn once,
    change OnTriggerStay2D() to OnTriggerEnter2D()

    to make the iceball spawn immediately change the Coroutine

    IEnumerator TrapDelay ()
    {
    Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    yield return new WaitForSeconds (TrapSpawnTime);
    }
     
    Last edited: May 16, 2018
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I think you're looking for @johne5 's answer, but with setting the coroutine to null at the end of (itself).

    Alternatively, you could spawn the ice ball at the start of the coroutine, and then create a loop with the delay + another spawn (not setting it to null there, but just leaving the stop/setting to null on trigger exit).

    Does that sound right?
     
  7. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    That's very close to what I want but for some reason the code @johne5 provided immediately spawns two ice balls rather than just one, why is that? The coroutine then works correctly afterwards.

    Oh wait actually, yes, it must be because of the collisionstay I'm using like you say one moment.

    Edit: The code works I think and is what I'm after but I have this other problem looking at my Debug.Log where the trap activates then immediately deactivates itself even though the player hasn't left the collider.
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I have no idea. I missed an obvious comment -- even missed reading it in johne's answer.. switching to OnTriggerEnter.
    Code (csharp):
    1. IEnumerator TrapDelay() {
    2.    WaitForSeconds wfs = new WaitForSeconds (TrapSpawnTime);
    3.    Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    4.    while(true)
    5.    {
    6.       yield return wfs;
    7.       Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    8.    }
    9. }
    That's what I was thinking before.
     
  9. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    huh, well that's really strange, it's now activating itself in the way I was wanting, however when I exit the collider, the coroutine doesn't stop.

    Edit: Think I figured it out, for some reason with this code my prefabs aren't updating themselves properly.

    LOL! I just worked it out, I hadn't hit apply on my prefabs so the code changes weren't going in right! :p

    @johne5's solution works, I just need to watch out with the collider placement now.
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    That is strange, indeed. Sounds like a little debugging for you, because as far as I can see/imagine, the feedback & code provided should run so long as you've entered the trigger, but stop when exited. =)
     
  11. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Perhaps post your script with the Updated changes, just to be sure.. :)
     
  12. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    It's no problem, just to explain what happened clearly I hadn't placed the colliders or made them big enough, so my player was jumping into them and because the collider hadn't detected the player leaving the collider the coroutine carried on since the collider hadn't detected my player exiting that's what caused the majority of the problems.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class InstantiateIceBallTrap : MonoBehaviour {
    6.  
    7.     private Coroutine _spawnCoroutine = null;
    8.     public GameObject iceBallTrapEmpty;
    9.     public float TrapSpawnTime;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.        
    14.     }
    15.    
    16.     // Update is called once per frame
    17.     void Update () {
    18.  
    19.        
    20.     }
    21.  
    22.     void OnTriggerEnter2D ( Collider2D other )
    23.  
    24.     {
    25.  
    26.         if (_spawnCoroutine == null)
    27.        
    28.         {
    29.  
    30.             _spawnCoroutine = StartCoroutine ("TrapDelay");
    31.             Debug.Log ("Trap Activated");
    32.         }
    33.     }
    34.  
    35.     void OnTriggerExit2D ( Collider2D other )
    36.  
    37.     {
    38.         if(_spawnCoroutine != null)
    39.         {
    40.             StopCoroutine (_spawnCoroutine);
    41.             _spawnCoroutine = null;
    42.             Debug.Log ("Trap Deactivated");
    43.         }
    44.     }
    45.  
    46.  
    47.     IEnumerator TrapDelay ()
    48.     {
    49.         Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    50.         yield return new WaitForSeconds (TrapSpawnTime);
    51.     }
    52.  
    53.  
    54.  
    55.  
    56.  
    57. }
    This is exactly why I'm playtesting my game properly.
     
  13. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Cool, I'm glad your figured it out.

    Though, your posted code doesn't reflect what was talked about here & wouldn't continue to spawn ice balls? ;)
     
  14. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Just realised what you're talking about because I was wondering why it would deactivate itself immediately, I think I can work that problem out myself, probably just going to a require a simple if and else statement or something.

    hmm no it's something else, but I know what the problem is at least.
     
  15. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    If you scroll up, one of my posts has code which should do what you want. :)
     
  16. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    @Lethn, Can clarify what the new issue is? You're post is kinda ping ponging, it's fixed, it's not fixed, it's fixed again, it's not fixed. The coroutine is not completing in way you want? or the ice ball is not spawning the way you want?
     
  17. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class InstantiateIceBallTrap : MonoBehaviour {
    5.     private Coroutine _spawnCoroutine = null;
    6.     public GameObject iceBallTrapEmpty;
    7.     public float TrapSpawnTime;
    8.     private bool isActive = false;
    9.     // Use this for initialization
    10.     void Start () {
    11.      
    12.     }
    13.  
    14.     // Update is called once per frame
    15.     void Update () {
    16.      
    17.     }
    18.     void OnTriggerEnter2D ( Collider2D other )
    19.     {
    20.         Debug.Log("OnTriggerEnter has been called");
    21.         if (!isActive)
    22.         {
    23.             _spawnCoroutine = StartCoroutine ("TrapDelay");
    24.             Debug.Log ("Trap Activated");
    25.             isActive = true;
    26.         }
    27.     }
    28.     void OnTriggerExit2D ( Collider2D other )
    29.     {
    30.         Debug.Log("OnTriggerExit has been called");
    31.         if(isActive)
    32.         {
    33.             StopCoroutine (_spawnCoroutine);
    34.             Debug.Log ("Trap Deactivated");
    35.             isActive = false;
    36.         }
    37.     }
    38.     IEnumerator TrapDelay ()
    39.     {
    40.         //spawn the ice ball immediately when OnTriggerEnter2D has been ran
    41.         Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    42.        
    43.         //lets keep the coroutine active
    44.         while(true)
    45.        {
    46.           yield return new WaitForSeconds (TrapSpawnTime); //i assume this is set to 1 sec or less
    47.        }
    48.     }
    49.  
    50. }
     
  18. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You forgot to keep spawning in the loop. :)
     
  19. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    I don't picture him looking for multiple spawned ice balls. but if that's what he wants then yes, it needs to added to the loop.

    IEnumerator TrapDelay ()
    {
    //spawn the ice ball immediately when OnTriggerEnter2D has been ran
    Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);

    //lets keep the coroutine active
    while(true)
    {
    yield return new WaitForSeconds (TrapSpawnTime); //i assume this is set to 1 sec or less
    Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);

    }
    }
     
  20. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    That's just how I read post #3 in the thread...
     
  21. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I don't know why I'm causing so much confusion here lol let's try again now I've had a break, here's what I've got in mind

    Play enters collider > Ice ball spawns once immediately > if player is still inside the collider, coroutine starts and keeps spawning ice balls every few seconds infinitely > if the player leaves the collider > coroutine stops

    I don't know how I can explain it anymore than that, does that make sense?
     
  22. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Yep, the answer is in my post or johne's last post. :)
     
  23. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Yes I just checked out his code I'll test it out, thanks :p hope that post cleared things up though, I was wondering why the coroutine was deactivating immediately despite the OnTriggerExit, I need to learn more about Boolean use generally.
     
  24. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    The bool would work in much the same way as checking if the coroutine was null before starting/stopping. You could use either.
     
  25. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I swear this game is legitimately making me lose my mind at some points, just to be safe, I double checked that the positioning of my collider was all correct and that my code was right and it is. The while loop doesn't seem to be working, the player enters the collider and the coroutine starts but it then deactivates itself despite the player still being in the collider. @johne5 definitely has the right idea and it works but it only plays through the coroutine once rather than indefinitely.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class InstantiateIceBallTrap : MonoBehaviour {
    6.  
    7.     private Coroutine _spawnCoroutine = null;
    8.     public GameObject iceBallTrapEmpty;
    9.     public int TrapSpawnTime;
    10.     private bool isActive = false;
    11.  
    12.         void OnTriggerEnter2D ( Collider2D other )
    13.  
    14.         {
    15.    
    16.         if ( !isActive )
    17.  
    18.             {
    19.  
    20.                 _spawnCoroutine = StartCoroutine ("TrapDelay");
    21.                 Debug.Log ("Trap Activated");
    22.                 isActive = true;
    23.             }
    24.         }
    25.  
    26.    
    27.         void OnTriggerExit2D ( Collider2D other )
    28.  
    29.         {
    30.  
    31.         if (isActive)
    32.        
    33.             {
    34.                 StopCoroutine (_spawnCoroutine);
    35.                 _spawnCoroutine = null;
    36.                 Debug.Log ("Trap Deactivated");
    37.                 isActive = false;
    38.             }
    39.         }
    40.  
    41.  
    42.  
    43.     IEnumerator TrapDelay ()
    44. {
    45.         Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    46.  
    47.     while(true)
    48.  
    49.     {
    50.         yield return new WaitForSeconds (TrapSpawnTime);
    51.         Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    52.     }
    53.  
    54.  
    55. }
    56.  
    57.  
    58.  
    59. }

    Now why would that be? Have I placed the code wrong somewhere or is it something a bit trickier?​
     
  26. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Check if OnTriggerExit2D is being called (Debug.Log). Also, just for sanity's sake, make sure that the game object this script is on is not destroyed.
     
  27. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Ah! Now we've got an interesting debug.log here.

    You'll spot it right away but OnTrigger exit plays itself through twice, now why is that I wonder?
     
  28. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Hm, just add:
    Code (csharp):
    1. print("From : " + other.name + "  on this game object : " + gameObject.name);
    See what it says.
    Do you have more than 1 collider, maybe?
     
  29. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Are you talking about the colliders for the traps within the scene? Could they be causing a conflict?
     
  30. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    can you send us a screen shot of the trigger colliders. I want to see how big they are.
     
  31. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    you are not doing any object verification, so you leave any trigger collider,the exit will run.

    and what goameObject is this scripted attached to? I assume it's the player.
     
  32. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Indeed, a few more things to check. I have to go for a while, but if you are still stuck after checking and you can post a simple repro case, as a unity package in the thread, I'd be happy to look at it in a while when I'm back.
    Good luck ;)
     
  33. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
  34. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    The scene looks ok, you have 3 gameObjects. each one has the script attached. Where is the landing/stand on part of the cload? does the trigger extend lower then it.

    Added some more Debugs like methos wanted. Please try this code. post the log to the form.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class InstantiateIceBallTrap : MonoBehaviour {
    5.     private Coroutine _spawnCoroutine = null;
    6.     public GameObject iceBallTrapEmpty;
    7.     public int TrapSpawnTime;
    8.     private bool isActive = false;
    9.         void OnTriggerEnter2D ( Collider2D other )
    10.         {
    11.             Debug.Log("TriggerEnter From : " + other.name + "  on this game object : " + gameObject.name);
    12.             if ( !isActive )
    13.             {
    14.                 _spawnCoroutine = StartCoroutine ("TrapDelay");
    15.                 Debug.Log ("Trap Activated");
    16.                 isActive = true;
    17.             }
    18.         }
    19.  
    20.         void OnTriggerExit2D ( Collider2D other )
    21.         {
    22.             Debug.Log("TriggerExit From : " + other.name + "  on this game object : " + gameObject.name);
    23.             if (isActive)
    24.             {
    25.                 StopCoroutine (_spawnCoroutine);
    26.                 _spawnCoroutine = null;
    27.                 Debug.Log ("Trap Deactivated");
    28.                 isActive = false;
    29.             }
    30.         }
    31.         IEnumerator TrapDelay ()
    32.         {
    33.             Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    34.        
    35.             while(true)
    36.             {
    37.                 yield return new WaitForSeconds (TrapSpawnTime);
    38.                 Instantiate (Resources.Load ("Ice Ball"), iceBallTrapEmpty.transform.position, Quaternion.identity);
    39.             }
    40.         }
    41.  
    42. }
     
  35. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    It seems to be that there's some weirdness happening with the player itself, could it be that the code is detecting both the box on my player and the groundcheck circlecollider and that's why it's playing itself twice and not detecting the exit correctly?

    https://ibb.co/dS9n0d

    Here's how I've set up the player.

    Here are the platforms.

    https://ibb.co/cMdVLd
     
  36. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well, you should definitely check if it's the player on enter/exit.
    I think the way your colliders are setup it will be okay, though the ground check is slightly lower.. that's up to you, if you want to change that.
    But you don't want the ice ball to 'exit' and stop your coroutine, for sure.
     
  37. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    it should be as simple as this, assuming you have the player tagged as the Player
    Code (CSharp):
    1. void OnTriggerEnter2D ( Collider2D other )
    2.         {
    3.             Debug.Log("TriggerEnter From : " + other.name + "  on this game object : " + gameObject.name);
    4.             if ( !isActive && other.CompareTage("Player"))
    5.             {
    6.                 _spawnCoroutine = StartCoroutine ("TrapDelay");
    7.                 Debug.Log ("Trap Activated");
    8.                 isActive = true;
    9.             }
    10.         }
    11.  
    12.         void OnTriggerExit2D ( Collider2D other )
    13.         {
    14.             Debug.Log("TriggerExit From : " + other.name + "  on this game object : " + gameObject.name);
    15.             if (isActive && other.CompareTage("Player"))
    16.             {
    17.                 StopCoroutine (_spawnCoroutine);
    18.                 _spawnCoroutine = null;
    19.                 Debug.Log ("Trap Deactivated");
    20.                 isActive = false;
    21.             }
    22.         }
     
    Lethn likes this.
  38. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Oh wow! That's working perfectly! Well how weird, it looks like it was the colliders that were causing the biggest issue but that does make sense, thanks for your helps guys. Ooo nice, my game has gotten more challenging now too because I can tweak things a lot easier as expected.

    So I guess I just need to make sure above all else that I'm using tags rather than simply grabbing any old collider if I've got a complicated player object to prevent any conflicts in the engine.
     
  39. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    good to hear that we final got it worked out. feel free to remove the Debugs and ask any questions that you might have.

    and in the TriggerExit. you can remove the _spawnCoroutine = null; this isn't used any more.