Search Unity

Destroying an Object Before Calling Coroutine

Discussion in 'Scripting' started by clamum, Jun 8, 2021.

  1. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    I'm really confused how to do this seemingly simple thing and cannot figure it out. I'm probably not understanding something about coroutines, and that's fine and I'd like to learn more, but I've tried to figure it out myself, and have done searching and reading about this, but still haven't gotten it.

    What I'm trying to do is destroy an object before calling a coroutine that continues my game. What I really want to do is make the object disappear from the screen; either Destroy or setActive(false) are fine, but both cause the same problem/result.

    The gist of the game is a bird flies above a city and every few seconds lets out a "poop" object that falls to the ground. The goal is to hit people that are walking around the city. When the "poop" object hits something, I want it to disappear, wait 2 seconds, then camera goes back to the bird flying and pooping and this continues until the timer expires.

    What's happening now, with trying to destroy the "poop" when it hits something, is the game's main loop gets all messed up and the bird will start pooping rapidly, instead of: pooping once, waiting until the camera follows the poop to the ground and waiting two seconds, then going back up to the flying bird and doing it again. The timing is all messed up.

    The "poop" object has code to allow the player to guide it, but the relevant is the OnCollisionEnter, which is only a stub to my main GameManger (I thought this might resolve the issue because the Destroy used to be in this method, which was in a script attached to the poop object that's actually getting deleted, but it didn't resolve the issue):
    Code (CSharp):
    1. void OnCollisionEnter(Collision collision)
    2. {
    3.     gameManager.OnCollisionEnter(collision, OriginPosition, hasPlopped, hasPersonCollisionOccurred);
    4. }
    The GameManger has most of the game's logic. Here's the relevant part of its OnCollisionEnter() method:
    Code (CSharp):
    1. public void OnCollisionEnter(Collision collision, Vector3 OriginPosition, bool hasPlopped, bool hasPersonCollisionOccurred)
    2.     {
    3.         if (collision.gameObject.tag == "Person")
    4.         {
    5.                 Destroy(CurrentPlop); // this is new and is not working right
    6.  
    7.                 StartCoroutine(WaitForPlop(4));
    8.             }
    9.         }
    10.         // plop hits the environment boundary (out of bounds), just reset the plop
    11.         else if (collision.gameObject.tag == "Boundary")
    12.         {
    13.                 Destroy(CurrentPlop); // this is new and is not working right
    14.  
    15.                 // 2 s
    16.                 StartCoroutine(WaitForPlop(2));
    17.             }
    18.         }
    19.         else
    20.         {
    21.                 Destroy(CurrentPlop); // this is new and is not working right
    22.  
    23.                 StartCoroutine(WaitForPlop(2));
    24.         }
    25. }
    26.  
    27. IEnumerator WaitForPlop(int numberOfSeconds)
    28.     {
    29.         yield return new WaitForSeconds(numberOfSeconds);
    30.  
    31.         DoAPlop(false);
    32.     }
    33.  
    34. public void DoAPlop(bool firstPlop)
    35.     {
    36.         if (!isRoundFinished)
    37.         {
    38.             if (CurrentPlop != null)
    39.             {
    40.                 MainCamera.ObjectToFollow = null;
    41.  
    42.                 Destroy(CurrentPlop); // this has always been here and has worked
    43.             }
    44.  
    45.             StartCoroutine(PlopAfterSeconds(SecondsBetweenPlops));
    46.         }
    47.     }
    This is the relevant, IMO, code. There's obviously more but I'm trying to keep this is simple as possible while retaining the details. As you can see, there's actually a Destroy before a StartCoroutine() in this "DoAPlop" method, which has always been there, and has worked fine. But this particular Destroy() was actually called AFTER the those "StartCoroutine(WaitForPlop(numSeconds))" calls in the OnCollisionEnter.

    The ONLY place this "poop" object is referenced in the entire game is: In the "DoAPlop" method, and in the "OnCollisionEnter" method in the GameManger (except of course in the script attached to the "poop" object itself).

    I really hope this makes sense. Like I've said, I've tried to figure this out and have done research but I just can't figure it out, and it seems so simple. I'm a Unity noob (my first game), but have been doing C# software development for many years.
     
  2. bobisgod234

    bobisgod234

    Joined:
    Nov 15, 2016
    Posts:
    1,042
    Add Debug.Log statements inside "DoAPlop". When it starts breaking (I would expect DoAPlop to get called rapidly over and over), check the stack trace for anything out of the ordinary. Add Debug.Log statements anywhere DoAPlop is being invoked etc etc. Log variable values and check they are what you expect them to be.
     
  3. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Coroutines can only run on active GameObjects. Destroying or deactivating an object implicitly stops all coroutines it is running.

    In this case, since it looks like all you want to do is call another function after a delay, you could probably use Invoke instead.

    In the more general case, the typical solution would be to run the coroutine on some other object.
     
    angrypenguin likes this.