Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Coroutine not starting

Discussion in 'Scripting' started by Wantcha, Dec 21, 2017.

  1. Wantcha

    Wantcha

    Joined:
    Dec 2, 2017
    Posts:
    114
    I am trying to make a script that fades between scenes. The "fade-in" works just fine, but when I try to fade out, it all breaks down.

    Right now, I have the following functions that handle the "fading-out" and the RestartScene() is assigned to a button. Consequently, when the button is pressed, the scene should fade out and reload itself, but it doesn't. Here is the code:

    Code (CSharp):
    1.  
    2.     public void RestartScene()
    3.     {
    4.         StartCoroutine("Restart");
    5.     }
    6.  
    7.     public IEnumerator Restart()
    8.     {
    9.         float fadeTime = GetComponent<Fading>().BeginFade(1); //gets the fadingTime and starts the fade
    10.         yield return new WaitForSeconds(fadeTime); //waits for the fade to end
    11.         SceneManager.LoadScene(SceneManager.GetActiveScene().name); //reloads the scene
    12.         Time.timeScale = 1f; //resumes the timeScale (the game is frozen when the button appears)
    13.     }
    I tested and the coroutine is not starting AT ALL.
     
  2. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    Code (CSharp):
    1. public void RestartScene()
    2.     {
    3.         StartCoroutine(Restart());
    4.     }
    5.     IEnumerator Restart()
    6.     {
    7.         Debug.Log("Restart is running");
    8.         float fadeTime = GetComponent<Fading>().BeginFade(1); //gets the fadingTime and starts the fade
    9.         yield return new WaitForSeconds(fadeTime); //waits for the fade to end
    10.         SceneManager.LoadScene(SceneManager.GetActiveScene().name); //reloads the scene
    11.         Time.timeScale = 1f; //resumes the timeScale (the game is frozen when the button appears)
    12.     }
     
  3. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    What's the coroutine attached to? When you change the scene, it clobbers gameobjects. If the coroutine was attached to a gameobject that got clobbered, it dies.

    You'll need to start the coroutine using a gameobject that's been marked DontDestroyOnLoad
     
    hayetham, BeyondMASC and 2ndGarrison like this.
  4. Wantcha

    Wantcha

    Joined:
    Dec 2, 2017
    Posts:
    114
    Did this and the message gets shown in the console, so why isn't the rest of the code working?
     
  5. Wantcha

    Wantcha

    Joined:
    Dec 2, 2017
    Posts:
    114
    Did that and it's still not working
     
  6. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    run this and lets see where it stops.
    Code (CSharp):
    1. public void RestartScene()
    2.     {
    3.         StartCoroutine(Restart());
    4.     }
    5.    
    6. IEnumerator Restart()
    7.     {
    8.         Debug.Log("Restart is running");
    9.         float fadeTime = GetComponent<Fading>().BeginFade(1); //gets the fadingTime and starts the fade
    10.         Debug.Log("fadeTime is set to : " + fadeTime);
    11.         yield return new WaitForSeconds(fadeTime); //waits for the fade to end
    12.         Debug.Log("wait complete");
    13.         SceneManager.LoadScene(SceneManager.GetActiveScene().name); //reloads the scene
    14.         Debug.Log("Scene Load complete");
    15.         Time.timeScale = 1f; //resumes the timeScale (the game is frozen when the button appears)
    16.         Debug.Log("reset time complete");
    17.     }
     
  7. Wantcha

    Wantcha

    Joined:
    Dec 2, 2017
    Posts:
    114
    I managed to fix it. The problem was that I was setting the timeScale to 0 before the yield, so it was going forever. Now, I have another problem :D. When I reload the scene, the colliders stop signaling the OnCollisionEnter2D function.
     
    Keepps65, Aestial and hjubris like this.
  8. 2ndGarrison

    2ndGarrison

    Joined:
    Sep 18, 2015
    Posts:
    3
    This helped me resolve my issue. I had an object with several child elements. I was trying to destroy all the child elements and then destroy the parent element. I was calling the destroy for the child elements on a coroutine, but for the parent, I was simply calling the destroy function right there.

    My coroutine for the child objects wasn't starting according to the debugger, but when I commented out the line for destroying the parent object, lo and behold! the coroutine started. I'll probably load the destroy parent in a coroutine as well to make sure the child has time to finish.
     
  9. pixel_123

    pixel_123

    Joined:
    Jan 14, 2019
    Posts:
    1
    I would put a Debug.Log("Function Run") in the function o see if you are not calling the function, because the corouine looks fine
     
  10. grahamlynch

    grahamlynch

    Joined:
    Jan 17, 2016
    Posts:
    3
    This happened to me when Coroutine X in scene B appeared not to start after a script in scene A loaded scene B. The problem was I had a coroutine in Scene A and (unknowingly) with the same name as the coroutine in Scene B. When I made the names different it worked.
     
    C-Gabriel and cgascons like this.
  11. C-Gabriel

    C-Gabriel

    Joined:
    Jan 27, 2016
    Posts:
    114
    Thanks for this! I had the same issue, basically having 2 coroutines with the same name, but in different objects of different types. First one starts, second one starts only in the editor. Coroutines are a joke...
     
  12. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    Coroutines are simply a tool. They have very specific uses. They work in very predictable ways. They are not a good solution for somethings things, but not others. In fact most things should NOT be a coroutine.

    If you have not yet understood them, here's some reference links to help you learn.

    Coroutines in a nutshell:

    https://forum.unity.com/threads/des...en-restarting-the-scene.1044247/#post-6758470

    https://forum.unity.com/threads/proper-way-to-stop-coroutine.704210/#post-4707821

    Splitting up larger tasks in coroutines:

    https://forum.unity.com/threads/bes...ion-so-its-non-blocking.1108901/#post-7135529

    Coroutines are NOT always an appropriate solution: know when to use them!

    "Why not simply stop having so many coroutines ffs." - orionsyndrome on Unity3D forums

    https://forum.unity.com/threads/things-to-check-before-starting-a-coroutine.1177055/#post-7538972

    https://forum.unity.com/threads/heartbeat-courutine-fx.1146062/#post-7358312

    Our very own Bunny83 has also provided a Coroutine Crash Course:

    EDIT:
    Here's the new link
     
    Last edited: Aug 3, 2023
    Bunny83 likes this.
  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    You should update your "blurp" :) UnityAnswers is gone and the redirect doesn't work. Here's the new link.

    Well, don't start coroutines with a string. Unity should have removed that long ago. You start coroutines by invoking the actual generator method and then pass it to StartCoroutine. Any kind of string binding comes with tons of potential issues and performance hits. So you're just using it wrong.
     
    Kurt-Dekker likes this.
  14. C-Gabriel

    C-Gabriel

    Joined:
    Jan 27, 2016
    Posts:
    114
    Kurt-Dekker , I agree with most of the things that you mentioned, but most people are still using coroutines in Unity for delayed calls because Unity lacks a good timers manager, like Unreal Engine has https://docs.unrealengine.com/5.2/en-US/API/Runtime/Engine/FTimerManager/ . I think a timers manager is quite essential and am surprised Unity doesn't have one that's not based on strings (for obvious reasons). Saying coroutines are predictable considering they need to have different name signatures despite them being in 2 separate classes, is a bit of a stretch.

    Code (CSharp):
    1. // in the editor, both start, but on an android device only 1 starts
    2.  
    3. public class ClassA : MonoBehaviour
    4. {
    5.     protected void Start() {
    6.         StartCoroutine(DoSomething());
    7.     }
    8.  
    9.     private IEnumerator DoSomething()
    10.     { ... }
    11. }
    12.  
    13. public class ClassB : MonoBehaviour
    14. {
    15.     protected void Start() {
    16.         StartCoroutine(DoSomething());
    17.     }
    18.  
    19.     // if this is renamed to something else, it works
    20.     private IEnumerator DoSomething()
    21.     { ... }
    22. }
     
  15. C-Gabriel

    C-Gabriel

    Joined:
    Jan 27, 2016
    Posts:
    114
    I was not starting them using a string (didn't even know one could do that), please check the post above. Let me know if there's something wrong there
     
  16. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    I highly doubt that. Coroutines are actually internal compiler generated classes. Calling a coroutine / generator method is nothing different from calling any other method. Though coroutines / generators do not run their code but return a new instance of that internal class which represents your coroutine code as a state machine. It's highly unlikely that generators from different classes would clash in any way. This is not even Unity related as the yield keyword which is responsible for that is 100% C#. So I would say you misinterpreted your results and your issue is elsewhere.

    Note that you can run coroutines on different MonoBehaviours since the actual IEnumerator object that is returned is a closure / object instance which can be run by any active and enabled MonoBehaviour. Maybe you disabled the MonoBehaviour that was running the coroutine which would terminate the coroutine?

    You could even run coroutines cross-over. So even this does work:

    Code (CSharp):
    1. public class ClassA : MonoBehaviour
    2. {
    3.     public ClassB b;
    4.     protected void Start() {
    5.         StartCoroutine(b.DoSomething());
    6.     }
    7.     public IEnumerator DoSomething()
    8.     { ... }
    9. }
    10. public class ClassB : MonoBehaviour
    11. {
    12.     public ClassA a
    13.     protected void Start() {
    14.         StartCoroutine(a.DoSomething());
    15.     }
    16.     public IEnumerator DoSomething()
    17.     { ... }
    18. }
    Here the Start of ClassA would start a coroutine on its own instance but runs coroutine DoSomething from instance b. ClassB on the other hand runs the coroutine DoSomething of "a" on his instance. As I said, the actual "DoSomething" method literally has only one line of code which is a return statement which returns a new instance of the internal statemachine class. It's very very unlikely that C# would mess that up.

    Note that you can write your own IEnumerator classes and pass them to StartCoroutine. Unity doesn't care what that class is you pass in. As long as it's implementing the IEnumerator interface it works. That means you can even use a List as "coroutine", though that would be pretty useless ^^.

    Code (CSharp):
    1. List<object> stuff = new List<object> {null, null, new WaitForSeconds(5), null };
    2. StartCoroutine(stuff.GetEnumerator());
    This would start a coroutine that first waits 2 frames, then waits 5 seconds and then another frame until it finishes. Of course there's no code in between the "yields" so it's completely useless. But it does work anyways.

    Whatever your issue was, I'm 99.99% sure it's not an issue with the coroutine / generator method itself. If the compiler would mess those fundamental things up, nothing would ever work.

    So you hijacked this thread for your own different issue? :) Because that's what the OP was about.

    Note that you can actually check what each generator returns. Use GetType on the object it returns before passing it to any StartCoroutine call. See the type name of that internal class (which is quite weird as you will see). StartCoroutine doesn't care and doesn't even know the name of the generator method. All it gets is the statemachine that was created by the generator method.

    Code (CSharp):
    1. IEnumerator obj = DoSomething(); // create statemachine object
    2. StartCoroutine(obj); // hand this instance to Unity to start it as a coroutine.
    So the name of the method can't have an influence here. The string version is different as it can only start coroutines which are defined in the same class. Also you can't pass arguments to the generator method. The string version in general is a bad idea.

    For the future, please start a separate thread and include more details about your issue, how your setup looks like and what your exact results are. Saying "it doesn't work" is insufficient. I'm also very sceptical about @grahamlynch's statement
     
    tomfulghum likes this.
  17. C-Gabriel

    C-Gabriel

    Joined:
    Jan 27, 2016
    Posts:
    114
    Ok, I made a simple project and the naming signature doesn't seem to have any impact, so I was probably doing something else wrong. Sorry for my rant
     
    Bunny83 likes this.
  18. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    Bunny83 likes this.