-
-
Notifications
You must be signed in to change notification settings - Fork 86
Description
I'm experimenting with parenting the Pause menu to scene tree root instead of current scene (top node just below the root) because I don't want to destroy the Pause Menu, I want to keep it alive across the scenes of my level.
I made a custom_pause_menu_controller.gd with the following change:
func _ready() -> void:
pause_menu = pause_menu_packed.instantiate()
pause_menu.hide()
# CUSTOM: Add Pause Menu to root, not `current_scene`, so that it is not destroyed when changing room
get_tree().root.call_deferred("add_child", pause_menu)However, I found that doing this would cause the Pause menu Restart to keep the game frozen (scene tree paused) for a very weird reason.
Note: this has other implications and causes other bugs but I'm aware of it and ready to fix true logical bugs related to keeping the Pause Menu alive under root. I'm just talking about a specific weird bug that I'm unsure how to fix here.
The issue seems to come from the Restart Confirmation popup:
I studied the callstack to find where the scene tree could be paused and found this in overlaid_window.gd:
func close() -> void:
if not visible: return
_scene_tree.paused = _initial_pause_stateThis code restores the paused state from the level under that window (previously saved in _overlaid_window_setup)
For the PauseMenu, it's false because the in-game before opening the menu was not paused.
For the RestartConfirmation, it's true because the PauseMenu just before had already paused the game.
In normal usage (without my patch, when Pause Menu is under current_scene), when confirming Restart, this is what happens:
- RestartConfirmation: reverts
_scene_tree.pausedto true - PauseMenu: reverts
_scene_tree.pausedto false
so game is not paused after Restart => OK
Callstack details:
- RestartConfirmation:
ConfirmationOverlaidWindow._on_confirm_button_pressed > confirm > confirmed.emit() > pause_menu.gd: _on_restart_confirmation_confirmed() > SceneLoader.reload_current_scene() > (RestartConfirmation node removal) > RestartConfirmation _exit_tree() > close() > _scene_tree.paused = _initial_pause_state (true) - Followed by
(PauseMenunode removal) > RestartConfirmation _exit_tree() > close() > _scene_tree.paused = _initial_pause_state (false)
With my patch, when Pause Menu is under root, it is not destroyed and therefore doesn't go through _exit_tree but only normal close() call instead. However the close methods are called in reverse order:
- PauseMenu: reverts
_scene_tree.pausedto false - RestartConfirmation: reverts
_scene_tree.pausedto true
so game is paused after Restart => not OK
Callstack details:
1. RestartConfirmation: `ConfirmationOverlaidWindow._on_confirm_button_pressed > confirm > confirmed.emit() > pause_menu.gd: _on_restart_confirmation_confirmed() > OverlaidWindow.close > _scene_tree.paused = _initial_pause_state (false)`
followed by a normal close:
2. RestartConfirmation: ... > confirm > close > _scene_tree.paused = _initial_pause_state (true)
So it looks like the normal behavior works but counting on node being removed to "force" the intended order (resolving the menu window stack in reverse order) whereas a normal execution would not call close in the desired order, causing the game to be frozen after restart.