Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
We use cookies to ensure that we provide you with the best possible experience on our site.
Your essential source on Unity and web development
Your essential source on Unity and web development
Unity’s timeScale defines the speed at which the game runs.
With a value of 1, time in the game flows as usual; at 0.5, it’s twice as slow; at 2, it’s twice as fast.
This parameter allows you to make slow-motion effects, acceleration, or pausing the game.
You can edit the timeScale using the following code: Time.timeScale = 0.5f;
In this example, game speed is divided by two.
Its value must not be negative.
timeScale demonstration on physics
However, it’s important to note that timeScale only applies to some Unity methods.
TimeScale acts on different systems in Unity:
Note that timeScale does not influence the frequency of Update function calls or the use of coroutine with yield return null.
However, timeScale affects Time.deltaTime whose value varies.Time.deltaTime
becomes equal to 0 with a timeScale of 0.
I recommend using Time.deltaTime
for time-based calculations to be frame-dependent and timeScale-sensitive.
It’s worth noting that the value of Time.fixedDeltaTime
does not move as a function of timeScale.
The timeScale only influences the number of FixedUpdate calls.
To sum up, the timeScale acts on all parts of your game that are frame-dependent.
TimeScale is the ideal candidate for pausing your game.
We need to set its value to 0: Time.timeScale = 0f;
Once again, the Update and Coroutine methods with yield return null continue to work.
Using Time.deltaTime
makes our animations immobile (as we multiply by 0).
However, for some performance-intensive calculations, it might be interesting to avoid calculations with a condition :
private void Update()
{
if (Time.timeScale == 0)
{
return;
}
// expensive calcul
}
Setting the timeScale to 0 when pausing a simple game works.
However, today’s games offer animations and effects, even while players pause the game.
Make way for our friend unscaledTime to solve this problem.
I’ve used pausing the game as an example, but all the functions presented below allow you not to depend on timeScale, whatever its value.
To continue running our coroutines without taking timeScale into account, we use the WaitForSecondsRealtime function instead of WaitForSeconds.
private void Start()
{
StartCoroutine(Animation());
}
private IEnumerator Animation()
{
while(true)
{
// rotate object each 2.5 seconds
Rotate();
yield return new WaitForSecondsRealtime(2.5f);
}
}
To make our animations work independently of the timeScale, we associate the value “Unscaled Time” with the property “updateMode” of our animator.
Here’s how to do it by script:
private void Start()
{
GetComponent<Animator>().updateMode = AnimatorUpdateMode.UnscaledTime;
}
To continue animating using an Update method without depending on timeScale, we can use Time.unscaledDeltaTime
instead of Time.deltaTime
.
private void Update()
{
transform.Translate(Vector3.down * Time.unscaledDeltaTime);
}
To use Unity’s physics when our timeScale is 0.
We have to go through several steps:
SimulationMode.Script
to get the manual control.Simulate
, coupled with WaitForSecondsRealtime.private Scene? _sceneMenu = null;
private Coroutine _physicsCoroutine = null;
private bool _isPause = false;
// Toggle function pause / unpause
private void SetPause()
{
_isPause = !_isPause;
Time.timeScale = _isPause ? 0f : 1f;
if (_isPause)
{
// SceneParameter is mandatory, load phyMenu scene
var operation = SceneManager.LoadSceneAsync("phyMenu", new LoadSceneParameters(LoadSceneMode.Additive, LocalPhysicsMode.Physics3D));
operation.completed += (operationCompleted) =>
{
_sceneMenu = SceneManager.GetSceneByName("phyMenu");
// If the scene has just loaded but we're no longer paused, we delete it directly.
// Safety for click spammers
if (!_isPause)
{
RemoveSceneMenu();
return;
}
// Manual physics
Physics.simulationMode = SimulationMode.Script;
// Coroutine will simulate
_physicsCoroutine = StartCoroutine(SimulatePhysics());
};
return;
}
// end pause, we remove the additive scene
RemoveSceneMenu();
}
private IEnumerator SimulatePhysics()
{
while(true)
{
// Physics run with a timeScale at 0
_sceneMenu.Value.GetPhysicsScene().Simulate(Time.fixedDeltaTime);
yield return new WaitForSecondsRealtime(Time.fixedDeltaTime);
}
}
private void RemoveSceneMenu()
{
if (!_sceneMenu.HasValue)
{
return;
}
// stop physics coroutine
if (_physicsCoroutine != null)
{
StopCoroutine(_physicsCoroutine);
}
// back in default simulation mode
Physics.simulationMode = SimulationMode.FixedUpdate;
// remove the scene
// todo, check Unload is completed before stopping the pause.
SceneManager.UnloadSceneAsync("phyMenu");
_sceneMenu = null;
}