- Home /
Coroutine stops after while-loop
I have a singleton LevelManager loading a level, waiting for a script from the newly-loaded level to assign a gameObject to the LevelManager to then do stuff with it.
I have the following code:
//some gameObject calls the loadLevel coroutine
void somefunction(sceneToLoad){
StartCoroutine(LevelManager.Instance.loadLevel (sceneToLoad));
}
//snippet of LevelManager.cs
public GameObject levelPrepper = null;
public IEnumerator loadLevel(string levelName){
Application.LoadLevel (levelName);
while (!levelPrepper)
yield return null;
yield return StartCoroutine (waitForLevelPrepper());
print("yay");
//do stuff
}
//snippet of the levelPrep.cs:
void Awake(){
LevelManager.Instance.levelPrepper = gameobject;
}
The problem is, "yay" never gets printed. I've done some reading and found that this might happen when the gameObject carrying the coroutine is destroyed. However, LevelManager is definitely never destroyed during the process, so I'm at a loss.
How are you sure Level$$anonymous$$anager isn't being destroyed in the process? Are you calling DontDestroyOnLoad() on the gameObject? Have you tried adding a Level$$anonymous$$anager.OnDestroy() and setting a breakpoint? 10/10 times I have a probelm with coroutines being killed, it's due to the owning behaviour being deleted.
Answer by OncaLupe · Dec 09, 2015 at 07:59 PM
Does the new level being loaded have a LevelManager that's being destroyed by the singleton? If so are you certain that it's not changing the Instance variable before it does? Remember that Destroy() actually happens at the end of frame, so any code under it is still run.
Also are you sure it's not the waitForLevelPrepper() coroutine that's holding things up? The loadLevel coroutine will wait until it's finished before running the print() line. You can add a print() above that StartCoroutine() to check.
Finally in levelPrep, I'd add a print() to make sure it's actually being run. You're also trying to assign 'gameobject', shouldn't that be 'gameObject'? If it's a variable you made, double check that it's assigned itself.
Thank you for your reply! I fixed the 'gameobject' bit, I think it came up when I was changing the code before posting here. I did have a bunch of print statements in all of the functions to try and fish out the cause of the issue, Which is how I know that Level$$anonymous$$anager doesn't get destroyed, and that waitForLevelPrepper gets called and finishes according to plan. It turns out that what was causing the issue was that the gameObject that called the coroutine in the first place got destroyed. Apparently, even though the coroutine was from a different gameObject, it still got ter$$anonymous$$ated because the gameObject that initially called it got destroyed, which is a peculiarity I had no idea about and wasn't checking for.
I remember getting bitten by that same thing a while back and it taking ages to debug what was going on. Now, as a rule. I never make public coroutines to prevent the risk of them being called by other instances that may get destroyed. Ins$$anonymous$$d I make all my coroutines private and, if necessary, expose a public method that starts them from within the containing class. $$anonymous$$akes it much more easier to trace coroutines as they are running/stopped (which I haven't found any other way of tracing).
@zxubian:
Well, you have to understand what a coroutine actually is. A coroutine on it's own is just an object (not a method). You have to distinguish between the actual generator / iterator method which creates the IEnumerator object and Unity's coroutine scheduler which actually "drives" the coroutine.
So this line does two things:
StartCoroutine(Level$$anonymous$$anager.Instance.loadLevel (sceneToLoad));
It can be split up into this:
IEnumerator enumerator = Level$$anonymous$$anager.Instance.loadLevel (sceneToLoad);
this.StartCoroutine(enumerator);
The first line executes the generator method which returns the IEnumerator object that represents the generated statemachine for your coroutine. In the second line you use StartCoroutine on the calling object and hand over the enumerator to Unity's coroutine scheduler. The coroutine will always run on the $$anonymous$$onoBehaviour you called StartCoroutine on. If you want to run the coroutine on the Level$$anonymous$$anager object you can use:
Level$$anonymous$$anager.Instance.StartCoroutine(enumerator);
However it's usually better to provide a public "start method" for the coroutine inside the Level$$anonymous$$anager object.
public Coroutine LoadLevel(string aLevel)
{
return StartCoroutine( loadLevel( aLevel ) );
}
private IEnumerator loadLevel(string levelName)
{
//...
It's usually a good idea to return the Coroutine object if you want to wait for the coroutine inside another coroutine.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
What threads are used in a scene? 1 Answer
How do I make a coroutine from another script stop? 3 Answers
Unity C# Singleton? 6 Answers