Pause menu only activates every other time a scene is loaded,Pause menu only works every other time a scene is loaded
Hello,
I'm having an issue where my pause menu only activates every other time my gameplay scene is loaded. When starting the game from either the gameplay scene (build index: 1) or the main menu (build index: 0) the pause menu will function as expected (time freezes, HUD is disabled, pause menu UI enabled, controller line interactors enabled). However if the scene is reloaded (via going back to the main menu and back or upon player death), these attributes won't trigger until the scene is reloaded once more. This continues to go back and forth forever. I can also trigger the pause menu on and off properly multiple times within the same scene instance while it is working properly.
Getting more specific, in the instances where the pause menu doesn't work, the Inspector shows the correct game object references. When the menu button on the controller is pressed, the Debug.Log information displays the behavior that is expected, but this is not reflected in the gameplay. Example: Debug.Log("Time Scale: " + Time.timeScale) will display "0" or "1" when it's supposed to, but the time in-game will not freeze. Also Debug.Log("HUD UI " + hudCanvas.activeSelf) will display "false" when expected, but the Canvas will still be visible in-game. It feels as though every other time the scene is loaded, it obtains references to the wrong instances of the GameObjects (including Time).
Here is the code for the Pause Menu (using Unity 2021.2.11f1):
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.XR.Interaction.Toolkit;
public class PauseMenu : MonoBehaviour
{
[SerializeField] GameObject pauseMenuUI;
[SerializeField] GameObject rightHandController;
[SerializeField] GameObject leftHandController;
[SerializeField] GameObject hudCanvas;
bool gamePaused = false;
public void ToggleMenu() // Triggered from Player script by controller button
{
if (gamePaused)
{
Resume();
}
else
{
Pause();
}
}
void Resume()
{
hudCanvas.SetActive(true);
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gamePaused = false;
SetControllerProperties(false);
}
void Pause()
{
hudCanvas.SetActive(false);
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
gamePaused = true;
SetControllerProperties(true);
}
void SetControllerProperties(bool isEnabled)
{
rightHandController.GetComponent<LineRenderer>().enabled = isEnabled;
rightHandController.GetComponent<XRInteractorLineVisual>().enabled = isEnabled;
leftHandController.GetComponent<LineRenderer>().enabled = isEnabled;
leftHandController.GetComponent<XRInteractorLineVisual>().enabled = isEnabled;
Debug.Log("HUD UI " + hudCanvas.activeSelf);
Debug.Log("Enabled: " + isEnabled);
Debug.Log("Time Scale: " + Time.timeScale);
}
public bool GetGamePaused()
{
return gamePaused;
}
public void MainMenu() // Triggered by UI Button Press
{
Time.timeScale = 1f;
SceneManager.LoadScene(0);
}
}
Answer by Xepherin · Mar 24 at 04:47 PM
I found my issue was with my Player script rather than my Pause Menu. Since the XR Interaction Toolkit requires the new Input System, I needed to unsubscribe from the pause menu action when the script was destroyed. This is something that not all Input System tutorials go over. I have provided the following code for example. And thanks @rh_galaxy for taking the time to help out.
public class Player : MonoBehaviour
{
[SerializeField] InputActionReference pauseAction;
void Start()
{
pauseAction.action.performed += PauseGame;
}
//MUST INCLUDE
void OnDestroy()
{
pauseAction.action.performed -= PauseGame;
}
}
Answer by rh_galaxy · Mar 24 at 12:58 AM
Why don't you do Resume() before LoadScene(0)?
public void MainMenu() // Triggered by UI Button Press
{
Resume();
SceneManager.LoadScene(0);
}
If it still doesn't work maybe there exist multiple PauseMenu objects, how are you loading the scenes except for MainMenu()? Do you use DontDestroyOnLoad() on any gameObjects?
Answer by Xepherin · Mar 24 at 01:15 AM
@rh_galaxy using Resume() instead of Time.timeScale = 1is still rendering the same issue. as for using DontDestroyOnLoad(), I have a GameData object that is carrying a current score and high score. It's only external interactions are when the player calls its AddToScore(int value) function and when it calls an "UpdateScore()" function from another object that references the score text within the HUD Canvas.
The object or component containing the PauseMenu script must belong to a scene also. Each time you load a scene all objects from other scenes are unloaded including the PauseMenu itself. Can you show the code where you create the PauseMenu?
Try adding this to the PauseMenu, you can also print the id with all the other logs.
static int id = 0;
void Start()
{
id++;
Debug.Log("PauseMenu: Start " + id);
}
The PauseMenu script is placed on the Pause Menu Canvas which has a Child object that contains the rest of the pause menu elements. This child is the object referenced as pauseMenuUI in the script above. The player script goes through FindObjectOfType().TriggerMenu() every time it's called. Also just clarifying that the Player Object (containing the Player script), the Pause Menu Canvas (containing the PauseMenu script), the HUD Canvas, and both controller objects do not have singletones. They should all be destroyed and created again when the scene resets. After adding your suggested code, the ID does increment every time the scene is loaded.
Your answer
Follow this Question
Related Questions
garbage collection in between scene changes? 2 Answers
Get current scene number 3 Answers
Loading new scene, array looses elements after Start() is executed. 2 Answers
How can you queue a sequence of multiple scenes... 0 Answers
SceneManager.sceneLoaded returns scene.isLoaded as false, is this intended? 1 Answer