- Home /
How to make WaitForSeconds work as a repeating/looping timer?
Hey folks. WaitForSeconds is bugging me again... I'm currently making a object pool that repopulates (re-enables) deactivated game objects every second or so.
Normally I'd use something like,
public float timerLimit = 1.0f;
private float timer = 0.0f;
void Update () {
timer += time.deltatime;
if(timer >= 1.0f){
Debug.Log("Do stuff to populate level!");
timer = 0.0f;
}
}
...but I'm trying to break some old habits and use coroutines instead (I've been warned off using Invoke, as coroutines are more flexible).
However, upon using,
public float populationRate = 1.0f;
// Update is called once per frame
void Update () {
StartCoroutine(SpawnLoop());
}
IEnumerator SpawnLoop () {
yield return new WaitForSeconds (populationRate);
Debug.Log ("Waited for " + populationRate + " seconds, and it's now " + Time.time);
}
...and expecting a log like,
Waited for 3 seconds, and it's now 3.000000
Waited for 3 seconds, and it's now 6.000000
Waited for 3 seconds, and it's now 9.000000
Waited for 3 seconds, and it's now 12.000000
Waited for 3 seconds, and it's now 15.000000
...instead, I get a log like:
Waited for 3 seconds, and it's now 3.001297
Waited for 3 seconds, and it's now 3.072943
Waited for 3 seconds, and it's now 3.358437
Waited for 3 seconds, and it's now 3.466427
Waited for 3 seconds, and it's now 3.497489
My intent was to create a passive loop to check my current population and then do stuff - instead of actively consuming CPU time each Update (trust me, this isn't a premature optimisation).
Is WaitForSeconds unsuitable for this? Is there a way to make it loop?
If I use a 'while = true' or 'for' loop in the coroutine, isn't this basically the same as just sticking that code into Update and building a short timer?
Questions, Unity community! Questions! Do you have... answers?!?!
Many thanks in advance,
--Rev
I'm unsure if a lot of CPU time is used when waiting inside a loop inside a coroutine, but my guess is that it won't use much. I'd move your IEnumerator code inside a while=true loop and call it from the Awake() method ins$$anonymous$$d of Update() - calling it from update will just queue up a whole bunch of spawns waiting for 'populationRate' time to pass.
IEnumerator SpawnLoop ()
{
while(true)
{
yield return new WaitForSeconds (populationRate);
Debug.Log ("Waited for " + populationRate + " seconds, and it's now " + Time.time);
}
}
Why didn't you just answer the question, @Simon Larsen? :)
Thanks for pointing out the Update issue, Simon! Should have known better, but obviously didn't! Cheers!
Answer by iwaldrop · Jan 15, 2015 at 01:17 AM
You need to remember that in coroutines you are responsible for setting up a loop and ending it.
IEnumerator SpawnLoop()
{
while (enabled)
{
yield return new WaitForSeconds (populationRate);
Debug.Log ("Waited for " + populationRate + " seconds, and it's now " + Time.time);
}
}
The above code will stop executing when the MonoBehaviour is disabled.
Furthermore, you will be starting that coroutine every frame, which is a no-no, and is why you're seeing results spit out every frame after a specified wait. You have n coroutines all waiting for that time limit before spitting something out to the log. Instead, start it in one of either the Awake or Start methods.
In retrospect, obvious. If WaitForSeconds is just the timer and not a loop, I need to implement the loop myself - hence while(enabled) (nice refinement on while(true)!).
Your - and Simon's - comments on the placement of coroutines in Update are appreciated. I knew I shouldn't do that, just forgot - and now I've a solid example of why not to do that in the future.
Cheers!