Skip to content

Khemitron Industries

Amateur Game Dev, Gaming, and TTRPGs

Menu
  • Home
  • Game Dev
  • About
Menu

Managing Popups in Godot

Posted on 2025-02-11

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.

Godot's node search. In the search field is "popup" and the results are "Popup", "PopupMenu" and "PopupPanel"

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”.

Godot nodes in the Scene Editor. The root node is "World Map" and it has multiple children, two of which 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:

World map scene without any popups.

And with an open popup:

World map scene with an open popup. The screen behind the popup is darkened and there is a popup panel in the middle of the screen

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

Share on Social Media
facebook tumblr reddit emailwhatsapp

Like this:

Like Loading...

Recent Posts

  • Refactoring the World Map 2025-06-09
  • Using Themes to Enhance my Game 2025-05-19
  • Quality of Life Improvements – Nested Tooltips 2025-05-12
  • Improving Quality of Life 2025-05-05
  • Godot4.4 – Powerful Localisation with gettext 2025-04-27
©2025 Khemitron Industries | Design: Newspaperly WordPress Theme
%d