Setting timeScale to 0 doesn’t necessarily pause everything. It just makes anything that is dependent on timeScale slope out to zero.
If any movement is not based on timeScale, then you’re still going to get movement.
And inputs and the sort still function.
Timescale does not slow down the update process. Update still occurs as many times per second as the framerate.
So you’ll need to do more than just set the timeScale to 0.
Furthermore, while timeScale is 0, if your HUD is dependent on it, it too will freeze. So you’ll need to make it dependent on a different delta. Like Time.unscaledDeltaTime and Time.unscaledTime.
I’ll show you what I do to ‘pause’ my game if you want. It’s long, so I’ma spoiler it:
First off my game has ‘states’, one of which is GamePlayState, another is GamePausedState, these are part of a state machine on a GameObject in the level.
In the GamePlayState during Update I check if the pause button is pressed, if it is pressed I call ‘Pause’ on the state machine:
public void Pause()
{
if (this.Paused) return;
_stateManager.ChangeState<GamePausedState>();
}
This changes into my GamePausedState if we aren’t already in that state.
And this is what that does:
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using com.spacepuppy;
using com.spacepuppy.Audio;
using com.spacepuppy.Utils;
using com.apoc.Ui;
namespace com.apoc.Scenes.Levels
{
public class GamePausedState : AbstractLevelState
{
#region Fields
#endregion
#region Properties
#endregion
#region Methods
#endregion
#region AbstractLevelState Abstract Overrides
public override void OnEnterState(AbstractLevelState lastState)
{
SPTime.Normal.Paused = true;
AudioManager.Global.Pause();
Notification.PostNotification<GamePausedNotification>(this.Level, new GamePausedNotification(true), false);
if (this.Level.HUD != null) this.Level.HUD.StatsVisible = true;
}
public override void OnExitState(AbstractLevelState nextState)
{
SPTime.Normal.Paused = false;
AudioManager.Global.UnPause();
Notification.PostNotification<GamePausedNotification>(this.Level, new GamePausedNotification(false), false);
}
public override void UpdateState()
{
var inputDevice = Game.InputManager.FirstOrDefault() as ApocPlayerInputDevice;
if (inputDevice == null) return;
var input = inputDevice.GetCurrentState();
if (input.Pause == spacepuppy.Ui.ButtonState.Down)
{
this.Level.ReturnToGamePlay();
}
}
#endregion
private void OnGUI()
{
if (!this.IsActive) return;
var c = Color.black;
c.a = 0.5f;
GUI.color = c;
GUI.depth = int.MinValue;
GUI.DrawTexture(new Rect(0f, 0f, Screen.width, Screen.height), SPAssets.WhiteTexture);
}
}
}
You may notice I use a weird class called SPTime… I don’t like the unity Time class and wrote a wrapper around it so I can give Time an object identity. This is nice so I can pass references to which kind of time I want to use around. This is really great for things like my tweener and the sort where I can choose if the tween updates by time, unscaledtime, or even a custom scaled time. My HUD is actually dependent on one of these ‘custom’ scaled time objects.
You can find it here:
spacepuppy-unity-framework/SpacepuppyBase/SPTime.cs at master · lordofduct/spacepuppy-unity-framework · GitHub
So on enter of the state we pause ‘normal’ time. This is basically setting Time.timeScale to 0 (look in SPTime to see).
I pause my AudioManager.
And I then dispatch a notification into the scene telling the world that we just paused the game. This is captured by any entities that may need to stall out their processes if the game pauses. Inside my entity class (which all mobs and the player get) I have the notification consumed and it ‘stalls’ my entity:
private void OnGamePaused(object sender, GamePausedNotification n)
{
this.StallEntity((n.Paused) ? StallMode.Paused : StallMode.Active);
}
/// <summary>
/// Put the entity into a state where it's not considered active.
/// </summary>
/// <param name="active"></param>
public void StallEntity(StallMode mode)
{
_stallState = mode;
switch(_stallState)
{
case StallMode.Active:
if (this.AIContainer != null) this.AIContainer.SetActive(true);
if (this.CombatMotorContainer != null) this.CombatMotorContainer.SetActive(true);
if (this.MobileMotorContainer != null) this.MobileMotorContainer.SetActive(true);
if (this.MovementController != null) this.MovementController.Pause(false);
//if (this.AttributesContainer != null) this.AttributesContainer.SetActive(true);
if (this.EventHitBoxContainer != null) this.EventHitBoxContainer.SetActive(true);
break;
case StallMode.Paused:
if (this.AIContainer != null) this.AIContainer.SetActive(false);
if (this.CombatMotorContainer != null) this.CombatMotorContainer.SetActive(false);
if (this.MobileMotorContainer != null) this.MobileMotorContainer.SetActive(false);
if (this.MovementController != null) this.MovementController.Pause(true);
//if (this.AttributesContainer != null) this.AttributesContainer.SetActive(true);
if (this.EventHitBoxContainer != null) this.EventHitBoxContainer.SetActive(false);
break;
case StallMode.Animating:
if (this.AIContainer != null) this.AIContainer.SetActive(false);
if (this.CombatMotorContainer != null) this.CombatMotorContainer.SetActive(false);
if (this.MobileMotorContainer != null) this.MobileMotorContainer.SetActive(false);
if (this.MovementController != null) this.MovementController.Pause(true);
//if (this.AttributesContainer != null) this.AttributesContainer.SetActive(true);
if (this.EventHitBoxContainer != null) this.EventHitBoxContainer.SetActive(true);
break;
case StallMode.Dead:
if (this.AIContainer != null) this.AIContainer.SetActive(false);
if (this.CombatMotorContainer != null) this.CombatMotorContainer.SetActive(false);
if (this.MobileMotorContainer != null) this.MobileMotorContainer.SetActive(true);
if (this.MovementController != null) this.MovementController.Pause(true);
//if (this.AttributesContainer != null) this.AttributesContainer.SetActive(true);
if (this.EventHitBoxContainer != null) this.EventHitBoxContainer.SetActive(false);
break;
}
}
Depending the mode I turn on or off gameobjects that handle certain aspects of the entity. Those containers correspond to the gameobjects that are a child of my root entity gameobject. You can see this in this image: