WaitForSeconds and Time.timeScale = 0

So, it turns out that Unity’s WaitForSeconds is using scaled time, which means that you can experience all sorts of weirdness when waiting for a precise number of seconds and scaling time, say in a pause screen.

using System.Collections;
using UnityEngine;

public class Pause : MonoBehaviour
{
	void OnEnable()
	{
		Time.timeScale = 0f;
		StartCoroutine(Countdown(3));
	}		

	IEnumerator Countdown(int duration)
	{
		while (duration > 0)
		{
			duration --;
			yield return new WaitForSeconds(1f); // will wait for ever!
		}
		Time.timeScale = 1f;
	}
}

You might think that the code above should work as intended, but no, it will never finish and instead stall on the yield, as WaitForSeconds uses Time.deltaTime. It also means that if you have scaled your time to anything other than 1, it will give interesting results: 0.5 will make every second last 2 seconds instead.

The simplest solution I’ve come up with is to make a custom method for it, WaitForUnscaledSeconds which looks at Time.unscaledDeltaTime but otherwise functionally behaves differently. Like so:

using System.Collections;
using UnityEngine;

public class Pause : MonoBehaviour
{
	void OnEnable()
	{
		Time.timeScale = 0f;
		StartCoroutine(Countdown(3));
	}		

	IEnumerator Countdown(int duration)
	{
		while (duration > 0)
		{
			duration --;
			yield return WaitForUnscaledSeconds(1f); //note: there is no "new" keyword.
		}
		Time.timeScale = 1f;
	}

	IEnumerator WaitForUnscaledSeconds(float dur)
	{
		var cur = 0f;
		while (cur < dur)
		{		
			yield return null;
			cur += Time.unscaledDeltaTime;
		}
	}
}

Since the WaitForUnscaledSeconds is a method, we don’t instantiate it with the ‘new’ keyword, but instead just call it. In the example above, we could cook it down even more, as the Countdown method isn’t really needed, but I’ve left it since you would usually do other functionality in there.

To make it entirely identical to the WaitForSeconds, you would have to make a new class that extends YieldInstruction, but I didn’t really want to go that direction with it. You could also extract it to a static utility class rather than have it embedded in the particular monobehaviour here, but I’ll leave all the expansions up to you.

I hope this will help a few poor souls that are confused why their WaitForSeconds stopped working.

Leave a Reply

Your email address will not be published. Required fields are marked *