Coroutine not working how I think it would?
This scenario baffles me. Maybe it's because I don't fully grasp coroutines. Disclaimer: I have read the documentation on coroutines and watched the Unity tutorial.
So here's the code in question:
if (isSubmerged && !isGettingReadyToEmerge)
{
StartCoroutine(EmergeTimer(1.2f)); //don't call this when isGettingReadyToEmerge==true!
}
//...
IEnumerator EmergeTimer(float time)
{
print("waiting to move... " + frameCount);
yield return new WaitForSeconds(time);
print("moving now! - while gettingrdy was " + isGettingReadyToEmerge + " " + frameCount);
transform.position = playerTransform.position;
isGettingReadyToEmerge = true; //statement is here to prevent another call
}
Ok, so I would expect the IEnumerator above to never happen while isGettingReadyToEmerge is true. But here's some unity logs that proves me wrong:
what am I missing?
Add line 2 / Debug.Log("Calling routine") I think that will help you see how many coroutines you have launched on 4. Also do you have your console in collapse mode?
Answer by TBruce · Dec 10, 2016 at 01:39 AM
The following will clear things up for you. (I removed your print commands so you can see what I did easier)
if (isSubmerged && !isGettingReadyToEmerge)
{
StartCoroutine(EmergeTimer(1.2f)); //don't call this when isGettingReadyToEmerge==true!
}
IEnumerator EmergeTimer(float time)
{
isGettingReadyToEmerge = true; //flag to prevent coroutine from being called while it is being executed
yield return new WaitForSeconds(time);
transform.position = playerTransform.position;
isGettingReadyToEmerge = false; // coroutine is finished and can be called again
}
Thanks for the response, however all this does is prevent the coroutine from getting past the first line.
In your version, isGettingReadyToEmerge is set to true off the bat, preventing the code from entering the coroutine again due to the first if statement. I tested it just in case, and here's what happens:
To be clear, I want to enter the coroutine, finish it once, then move on with the code and leave that coroutine unused until a later time. In my original scenario, it was getting used once, then calling the end of it over and over.
After further contemplation of your answer, I've realized my key misunderstanding of coroutines - I thought that they needed to be called again to resume, but they apparently start a new parallel block of code completely unabated by the function that called it. Was not aware of this. I was able to get the code working by using code that looks like this:
if (isSubmerged && !isGettingReadyToEmerge && enter)
{
StartCoroutine(EmergeTimer(1.2f));
}
//...
IEnumerator EmergeTimer(float time)
{
enter = false;
yield return new WaitForSeconds(time);
isGettingReadyToEmerge = true;
transform.position = playerTransform.position;
}
Coroutines usually take the following form
IEnumerator $$anonymous$$yCoroutine()
{
// enter code that I want to be executed immediately here
// enter yield statement(s) here - execution is temporarily suspended
// enter code that I want to be executed before completion of coroutine here
}
One generally creates a flag to prevent a coroutine from being called while it is being exectuted. The flag is generally set on entry to the coroutine and before exit of the coroutine. The flag is checked before calling the coroutine to make sure that it is O$$anonymous$$ to enter.
In your original code you had the comment
// don't call this when isGettingReadyToEmerge==true!
hence setting isGettingReadyToEmerge
to true on entry to the coroutine and false on exit. There is no reason for the additional variable enter
unless you are using isGettingReadyToEmerge
somewhere else. If that is the case you could just as well do this
bool allowEntry = true;
if (isSubmerged && allowEntry) { StartCoroutine(EmergeTimer(1.2f)); //don't call this when isGettingReadyToEmerge==true! }
IEnumerator EmergeTimer(float time)
{
allowEntry = false; //flag to prevent coroutine from being called while it is being executed
yield return new WaitForSeconds(time);
transform.position = playerTransform.position;
allowEntry = true; // coroutine is finished and can be called again
}
unless you also need to check the additional flag.
The reason I need the additional flag is because a later part of the function actually uses "isGettingReadyToEmerge" - it has to become true only once the coroutine is complete and no sooner. That Is why I included "Enter" as the flag. I'm just learning how these things work. I've been using time.delta time for the past 3 months.