One of the features of the RPG course series involves the multiple windows of UI that could be open at any given time. These include
- InventoryUI and EquipmentUI (HidingPanel)
- DialogueUI
- ShopUI
- TraitStoreUI
- PauseMenuUI
While these windows are open, in all cases except the PauseMenuUI (introduced in Shops and Abilities), the game continues running while you’re using the UI. This can become a problem if you begin a dialogue and other characters come along and start attacking you, or if you’re in a mobile game where IsPointerOverGameObject doesn’t work the same way as within a PC or Mac game.
I find, an ideal behaviour in all cases is that when one of these interactive windows is open, the game should pause, and only restart when all of these windows are closed. What’s needed to make this happen is some sort of static registry that tracks when a window is open and when a window is closed.
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Class to pause and resume game by controlling the time scale in Unity.
/// </summary>
public class PauseWhenActive : MonoBehaviour
{
/// <summary>
/// Static dictionary holding active instances of PauseWhenActive class.
/// </summary>
private static Dictionary<PauseWhenActive, bool> activeInstances = new();
/// <summary>
/// Unity life-cycle event for handling actions at the time of enabling this component or GameObject.
/// It adds the current instance into the activeInstances and sets the time scale to 0 (Paused state).
/// </summary>
private void OnEnable()
{
activeInstances[this] = true;
Time.timeScale = 0;
}
/// <summary>
/// Unity life-cycle event for handling actions at the time of disabling this component or GameObject.
/// It removes the current instance from the activeInstances and if no more active instances are left,
/// it sets the time scale to 1 (Resume state).
/// </summary>
private void OnDisable()
{
if (activeInstances.ContainsKey(this))
{
activeInstances.Remove(this);
}
if (activeInstances.Count == 0)
{
Time.timeScale = 1;
}
}
}
By placing this script on each windows’s top GameObject (i.e. the topmost one that is opened or closed when the window is active), whenever one of these windows is opened, the game will be paused and you can interact with the window. Once all of the windows are closed, the game automatically resumes.
It does whis with the activeInstances registry
private static Dictionary<PauseWhenActive, bool> activeInstances = new();
We’re using a Dictionary simply to ensure that a unique entry exists for each window. While you could accomplish this with a List<PauseWhenActive>
, this solution ensures that there is no possibility of a double entry. It shouldn’t be possible to create a double entry by adding to a List, but best practices suggest using a way that guarantees no duplicates.
When a window is opened, the OnEnable adds the instance to the Dictionary. and pauses the game.
When it is closed, the OnDisable removes the instance from the Dictionary, and if there are no entries remaining resumes the game.
With this solution, I recommend editing the PauseMenuUI.cs script and removing the references to Time.timeScale. Let the PauseWhenEnabled handle this part. Then the PauseMenuUI can focus on saving and reloading the game. This will prevent the PauseMenuUI from restarting Time.timeScale while other windows are open.