The coroutine doesn’t change how the while loop behaves overall. It only adds a bit of logic to allow the while loop to pause so other code can do stuff, like update GUI in your case. So either something in your logic somewhere is doing something it shouldn’t (like turning off the gameobject or disabling the script) or you’re hitting some sort of error that is stopping your code possibly. Also make sure you aren’t forcing a break out of your while loop early either.
Are your Debug calls printing out expected values?
If that shows up, it quits early and that’s because timeLeft decrements too quickly (attached debugger and step through).
If it doesn’t show up, the coroutine exits early. Like Brathnann said, the GameObject or its parents could be set to inactive, or the script component disabled, or you call StopCoroutines/StopAllCoroutines.
hmm, i mean at first glance it looks like it should be working… Try using just ‘’ yield return null; ‘’, though i cant see any reason it would perform differently. Shrug. No pro here, I use very similiar time routines through some stuff , never had an issue getting the correct result. maybe something else is at play?
But unfortunately, I couldn’t pinpoint where the problem is, here is the following changes made to the code in order to test the hypotheses proposed here:
private IEnumerator DisplayScore(int time)
{
float timeLeft = time;
float buffer = 0.1f;
timeLeft += buffer;
char[] scoreChars = TreatScore(GetDisplayScore());
Debug.Log("The timeleft is: " + timeLeft);
while (timeLeft > 0)
{
scoreText.text = string.Concat(
(timeLeft > time * 1 ? scoreChars[0] : RandomNumber()),
(timeLeft > time * 0.8 ? scoreChars[1] : RandomNumber()),
(timeLeft > time * 0.6 ? scoreChars[2] : RandomNumber()),
(timeLeft > time * 0.4 ? scoreChars[3] : RandomNumber()),
(timeLeft > time * 0.2 ? scoreChars[4] : RandomNumber()),
(timeLeft > time * 0 ? scoreChars[5] : RandomNumber()));
Debug.Log(scoreText.text);
timeLeft -= Time.deltaTime;
Debug.Log("The timeleft is: " + timeLeft);
yield return new WaitForEndOfFrame();
}
Debug.Log("Ending the coroutine!");
}
I’ve included the code which has been commented in the previous post but doesn’t really change the way the coroutine should work, since it doesn’t appear to work even when is commented. Also, I included some Debug.Log(); in order to show the current state of the coroutine and While() loop
private void Update()
{
Debug.Log("This gameObject is currently active? " + this.gameObject.activeSelf);
Debug.Log("This script is currently active? " + this.enabled);
}
I also wrote some Debug.Log(); in the Update function of the same script, in order to know if either the gameObject or the script was being deactivated in the runtime.
Neither the gameObject nor the script has been deactivated during runtime.
The only somewhat clue I got about what could be going on is that I’m currently using animation in the same object in which the script is attached, I do turn it on through animation but I do not turn it off at any point after that.
There really is no reason to be using WaitForEndOfFrame(); here. That does a very specific thing that you are not trying to do here. If you want to wait one frame, simply use yield return null;
The Debug.Logs you put in Update are not all that informative. Why? As soon as your object is deactivated Update will stop running. You will never see a log that says “This gameObject is currently active? false”
I highly recommend turning Collapse OFF in your console window. It is obscuring the timing/ordering of your log messages.
It may be helpful to share the entire script here and also the code which starts the coroutine.
Since your coroutine is triggered from Awake, simply turning on the GameObject should trigger it. What happens when you just turn it on manually?
I never see your “Ending” message print out, which means it never finishes. That still seems to suggest the object is getting turned off, the script disabled, or a stop coroutine call is being triggered somewhere.
Make sure you error and warning logs are still visible, if those are disabled, you’re not going to see something that might be important.
I would add the OnDisable method to your script above, this will just double check that even if you directly aren’t calling it, if it does get turned off even briefly, you’ll see it. Note that if your object gets turned off, kills the coroutine, and then gets turned on again, the Awake isn’t going to run again.
I just came to the party and when I look at the coroutine, I’m just asking myself what’s the intention of that coroutine as it literally does nothing once it enters the while loop. Everything that’s inside the while loop is only affecting local variables which do not have any side effects on anything else. So what’s the exact intended usage of that coroutine?
Just to break down what actually happens inside your coroutine:
private IEnumerator DisplayScore(int time)
{
float timeLeft = time;
float buffer = 0.1f;
timeLeft += buffer;
char[] scoreChars = TreatScore(GetDisplayScore());
while (timeLeft > 0)
{
Debug.Log(timeLeft);
//Some logic I want to include in the score doesn't really change the coroutine behavior
Debug.Log(scoreText.text);
timeLeft -= Time.deltaTime;
Debug.Log(timeLeft);
yield return new WaitForEndOfFrame();
}
}
So the first 3 lines essentially boil down to this
float timeLeft = time + 0.1f;
Next comes the only line in your coroutine which may even do anything:
Here you call two methods. FIrst GetDisplayScore. We don’t know what it actually returns, but from the name probably the score as a numeric value or as a string. The second method that is called after that is your TreatScore method that takes whatever GetDisplayScore returns as argument and returns a char array. You store that char array in a local variable and don’t ever touch it again. So this is kinda pointless (unless there’s other code you did not show).
Next comes your while loop which just runs purely on local variables and you’re just printing out some debug log values.
I’ve just read your latest post where you at least give some information on which logs get through and also log properly (and not just logging single values). We can see 5 log messages with collapse enabled. Are there any other log messages? Any errors? If an error like a null reference exception would happen inside the coroutine it would be terminated. This could happen when you for example destroy that “scoreText” object. Trying to access it again would cause an error and the coroutine would stop running. If there’s no error you can see, the only think that’s left over I can think of is that you call StopAllCoroutines on your MonoBehaivour from somewhere or you somehow deactivate / disable it and re-enable it immediately again. Try adding a Debug.Log to the OnEnable / OnDisable callbacks to see when it’s enabled / disabled. If there is any disable happening after the coroutine has been started, it would be stopped.
This is not actually true, but it did somewhat help me in the long run (I’ll get to that later) If I were to simply call yield return null the coroutine would keep running the While() loop all in the same frame since it wasn’t specified for it to stop.
You’re right on this one, I sorta didn’t really care about the Debug.Logs inside the same script since I when for a FACT that neither the gameObject nor the script was being deactivated in the runtime, it was more of a way for me to show (quite poorly, yes) that they were active.
I don’t usually use the collapse on the console, I only chose to do so in this case in order to obtain a smaller screenshot while still getting my point across.
Thank you! It’s my first time here in the Unity forums (as you can probably tell by my post count) and sometimes is hard to give all of the information needed to help people help me. Unfortunately, I knew for a FACT that the gameObject wasn’t being turned off nor was the script.
I know it can be tough sometimes to help others with code. But if some sort of error message was appearing in the console I would most definitely include it here, and yes, there is a reason for this coroutine and While() loop, and while, sure, it might not be the most well-written code in history it is functional and is there for a reason.
So for the fun part now.
I know how I mentioned that I knew for a FACT that the gameObject wasn’t being turned off?
Well, I was wrong huge thanks to everyone who mentioned that!
It turn out that there was ONE frame where I turned on the gameObject through the script and the animation system kicked in where the gameObject would turn on and immediately be turned off via animation. The lesson here? I don’t take your knowledge for granted, I guess? Anyways thank you so much to everyone who took their time to try and help me!
And if anyone is wondering here is the final result: (The numbers in the purple-ish background)
Not true. yield return null; means wait one frame. it will not keep running in the same frame. To make it keep running in the same frame you’d remove the yield altogether.
Sorry, but this does not make much sense. @PraetorBlue is correct here and I don’t really understand your objection here. When you yield on WaitForEndOfFrame the coroutine would be resumed at the end of the current frame. If you’re currently at the end of the current frame, it would be resumed at the end of the next frame. There’s no reason for you to wait until the end of the frame as at this point everything has already finished. So no further rendering will take place. So everything you do in your coroutine would be delayed one frame. yield return null is the correct way to resume the coroutine every frame.
Currently you use yield return new WaitForSeconds(0.01f); which also makes no sense. This would only have any effect at framerates greater than 100fps. Any framerate lower would just be the same as yield return null;, just with additional garbage allocated. Also since you use Time.deltaTime to decrease your timer, you have to iterate the while loop once pre frame. So waiting for 0.01 seconds just makes no sense. It would mean you get inconsitent results on machines which may have high framerates (say 500 fps). In that case your coroutine would take 5 times longer.
So if there would be an error you would have posted it? how should we know that? We have thread here on a daily bases of people posting questions and do not mention that they actually got an error. Also you said your while loop has a purpose. Yes, the new code you posted actually does something, however your original code does not and I was referring to that. So here we have an example where you omitted information
ps: If you have trouble understanding how coroutines work behind the scenes, you can read through my coroutine crash course.
So, the end result is… You actually didn’t know for a fact?
Every programmer makes mistakes, even experienced ones. The biggest thing to remember is, question everything when it comes to bugs. Just because it shouldn’t be happening, doesn’t mean it’s not.
Oh, and also, remember we’re trying to help. We’re developers also. We’re empathetic to your bugs as we’ve been in your shoes before and sometimes still are! We are not personally attacking you, we’re just as understanding of the frustration and the desire to get something fixed as you are.
Glad you got it solved and now can move on with your developing.