- Home /
while loop not looping
I'm sure I've done this same thing many times before, but this time it doesn't appear to be working. The code will print Time only once and then will quit the function on the yield.
function Boost ()
{
var destTime = Time.time + 20;
while ( Time.time < destTime )
{
Debug.Log ( Time.time );
yield;
}
Debug.Log( "Loop complete" ); // <~~ This is never reached
}
There's nothing wrong with that code by itself; you're doing something elsewhere (maybe destroying the object or something).
Okay, I figured out what's causing it but I have no idea how or why.
What calls this function is an item. If the item is collected, it calls this function remotely telling the other script to Boost() and then deactivates itself. If the item doesn't deactivate itself after calling the function, the while loop runs correctly, but when it deactivates itself the loop runs once and quits at yield.
But why is this happening? The item that is deactivating itself is calling a function on a completely separate gameObject. Whether it is deactivated or not should have no bearing on the Boost() function above. :S
That's not any behavior I've seen, and I can't replicate it now. Whenever you come across something "weird", make the simplest possible repro case and see if the behavior happens. If not (which is nearly always), keep adding more logic from the original code until you find out where it's going wrong. This may seem time-consu$$anonymous$$g, but it will typically save you a lot of problems later on since you won't have to rely on hacks that may or may not "fix" the problem.
Answer by Jamora · Sep 29, 2013 at 11:39 AM
Coroutines are run on the GameObject that had the script that called StartCoroutine. Note that in UnityScript the StartCoroutine is implied, if the function has a yield in it.
So, let's say you have a player and an item. If you have the Player call Boost()
on the item, the Boost()
coroutine will be run on the Player's GameObject. If you have the Item call Boost()
, the coroutine will be run on the Item's GameObject.
You can see this behavior with the following scripts:
//MyBaseClass.cs
//Attached to a GameObject named "1"
using UnityEngine;
using System.Collections;
public class MyBaseClass : MonoBehaviour {
public void StartCor(){
StartCoroutine(Cor ());
}
public IEnumerator Cor(){
while(true){
Debug.Log("coroutine");
yield return null;
}
}
}
//MyExtendedClass.cs
//Attached to another GameObject
using UnityEngine;
public class MyExtendedClass : MonoBehaviour {
void Start(){
//StartCoroutine(GameObject.Find("1").GetComponent<MyBaseClass>().Cor());
GameObject.Find("1").GetComponent<MyBaseClass>().StartCor();
}
}
Depending on who called StartCoroutine, disabling that GameObject will also stop the coroutine.
Your problem will most likely be solved by calling Boost()
from somewhere else than the Item script.
"disabling that GameObject will also stop the coroutine"
Just to be clear for new chums, coroutines run independently of the enabled property of a component.
They just keep runnin' and runnin' if you enable=false a component
You have to SetActive(false) on the whole gameObject to stop a coroutine. Or, you can literally Destroy the component (the script attached, that is to say) in question.
So run the test below. In the editor in Inspector, turn on an off (enabled property) the check box on the Teste component. The coroutine keeps going with a life of its own.
In contrast turn off the whole gameObject (checkbox at top) and it finally stops. It's well worth noting that if you make it active again (same checkbox), the coroutine does not "carry on as it was".
If you are new it's worth remembering those two checkboxes in Inspector (for the gameObject, and in some cases for the individual components) are utterly different things.
So that's what Jamora is saying here.
using UnityEngine; using System.Collections;
public class Teste : $$anonymous$$onoBehaviour { void Start () { StartCoroutine(teste()); Invoke("blockme",10.5f); }
IEnumerator teste()
{
while (true)
{
if (enabled) Debug.Log("yeah it is enabled");
else Debug.Log("it's not enabled but I don't give a stuff I am still running.");
Debug.Log("yo I am running ......");
yield return new WaitForSeconds(0.25f);
}
}
void blockme()
{
enabled = false;
// but, try this .... DestroyImmediate(GetComponent<Teste>());
}
}
Thanks for the input, Jamora. Why is the original GameObject still a factor, even if it has nothing to do with the coroutine it called? Seems kinda strange.
It's appropriate for the Coroutine to end early if the GameObject the Coroutine is currently running on is made inactive. I don't see how it makes sense for the Coroutine to end early if the GameObject that called the function is made inactive. It's like your sidekick is dying at the end of the movie and with his last dying breath he says, "Push the button to stop the explosion! " and so the hero runs over to push the button but then stops and turns back, sees his sidekick die, and decides it doesn't need to be pushed anymore. :P
Answer by Kiloblargh · Sep 29, 2013 at 03:11 AM
The yield is probably the problem.
But that's not the right way to use a while loop, anyway. Either use InvokeRepeating or set a boolean (isBoosted) and do something in Update() if the boolean is true. Define your destTime variable in the main script, not in the function that sets it.
I try to avoid using Update() unless my script is doing something every frame (which this isn't). In retrospect I may do an invokerepeating next time I'd code something like this.
The yield is not a problem, and it's a perfectly acceptable way to use a while loop. Coroutines are generally easy to use and quite useful. The only thing I'd stay away from is Time.time, since that's a single-precision float, which degrades in precision over time. I'd generally stay away from Update unless necessary, since you'd be forced to use a global variable as you note (local is to be preferred where feasible), among other things.
Just curious Eric, what do you recommend ins$$anonymous$$d of using time?
It depends, but probably DateTime would be typical. Time.time is fine if your app won't ever run for very long, but in those cases where it might run for days or weeks at a time (like in a kiosk), it can be problematic.
Your answer
Follow this Question
Related Questions
Yield Not Working - While Loop 3 Answers
Have a function wait unitl a boolean is false 1 Answer
Re-using the "same" for loop? 1 Answer
How do I call a function containing a while loop, inside the update function? 2 Answers
Trying to Loop A Function 2 Answers