I recently ran into a problem with Godot: the built in way it handles popups is not particularly great for video games. Or, at least, it’s not how I want popups to work in my game.
Searching for “popup” in the Godot editor brings back three types of node.

It feels like Godot wants you to use these to display popups in your game. This is the behaviour if you use one and trigger its PopupShow
method:
- Popup appears. Hooray! This is what we wanted to happen!
- The player can interact with the popup. Awesome! We want to interact with out popups!
- The player can click off the popup to close it. Nice. Definitely useful in some situations.
- You can’t change the click-off-to-close behaviour. Uhh… sometimes I don’t want that to happen…
- The popup closes if the user alt-tabs out of the game. Huh… I usually don’t want that to happen…
I think I remember seeing a reddit post about changing the last of those behaviours, but I’ve not been able to find it again and it’s not a simple fix. Honestly, it feels like something that shouldn’t need fixing in the first place.
My Solution
So with that in mind, I’ve made my own popup manager that also pauses the game while the popup is open (because I want that behaviour), and then automatically unpauses once the popup’s been closed.
I currently have it in my World Map scene, but I might extract it to its own component since I’m sure I’ll find a use for it elsewhere. The important nodes here are “Overlay” and “PopupContainer”.


Since I want the background game to pause when a popup is open, but I still want the popup code to execute, I’ve set the process mode of the PopupContainer to “Always”. This ensures that it ignores pausing. I also want the parent node (WorldMap in this case) to process code even if the game’s paused too, so that’s been set to “Always”, while the other children (Timers and MapUI) have been set to “Pausable” so that they do Pause.
Overlay is just a simple full-screen ColorRect which tints the screen behind the popup a darker colour, indicating to the player that you can’t interact with it. PopupContainer is just a control which can be set to whatever your maximum popup size is and is anchored to the middle of the screen.
The code that controls my popups is this:
private Control _popupContainer;
private ColorRect _overlay;
public override void _Ready()
{
// grabs our PopupContainer and Overlay nodes
_popupContainer = GetNode<Control>("PopupContainer");
_overlay = GetNode<ColorRect>("Overlay");
}
// we call this manually when opening a popup
private void OpenPopup(Control popup, Action onPopupAdded = null)
{
// pauses the game
GetTree().Paused = true;
// tints the rest of the screen
_overlay.Show();
// adds the popup to the PopupContainer
_popupContainer.AddChild(popup);
// runs onPopupAdded if it's not null
onPopupAdded?.Invoke();
// sets up a lambda function that runs when the popup closes
void onPopupClosed() => OnPopupClosed(popup, onPopupClosed);
popup.VisibilityChanged += onPopupClosed;
}
// this gets called automatically when the popup's visibility changes
// e.g. from visibility = true to visibility = false
private void OnPopupClosed(Control popup, Action callback)
{
// stop the onPopupClosed lambda function from running if
// visibility changes again
popup.VisibilityChanged -= callback;
// hides the overlay that blocks the rest of the screen
_overlay.Hide();
// unpauses the game
GetTree().Paused = false;
}
One slight caveat is that all your popups need to manage hiding themselves, but I think that popups should be responsible for that anyway. I also have my popups free themselves from the tree, but you could modify this so that it works with nodes which are permanently in the tree.
An example of how I use this in my World Map script:
private void OnRegionRevealed(Region region)
{
// creates the popup
var popup = RegionScoutedNotification
.Instantiate<RegionScoutedNotification>();
// calls our open popup method with an action to
// set a region once the popup is in the node tree
OpenPopup(popup, () => popup.SetRegion(region));
}
Before the popup is opened:

And with an open popup:

Now I can add any further popups safe in the knowledge that they will behave in the same manner each time.