Disable input on a Screen when an Overlay is open

Godot Version

v4.5.stable.official [876b29033]

Question

I have a main scene with two nodes stacked on top of each other:

  • Screen: contains the main gameplay and UI.
  • Overlay: shows elements such as a prompt or inventory on top of the Screen. This node can be shown or hidden.

The Overlay has a transparent background that eats mouse clicks and blurs the Screen behind it.

Both the Screen and the Overlay have child nodes that handle button input. When the Overlay is visible, I want all input handling in Screen’s children to be disabled, without pausing the game.

What would be a clean way to disable _input(event) on all children of the Screen node?

Call set_process_input(false) on all of them.

2 Likes

Wouldn’t that be expensive on 1000s of child nodes? Out of which only a fraction care about input? I was hoping I could intercept the propagation at the root.

Then call it only on relevant nodes. You can intercept/consume only if using _unhandled_input() or Control nodes.

More details on propagation here:

2 Likes

Change the Overlay node from a Control to CanvasLayer.

Thanks for the suggestion. Could you elaborate how this will prevent any controller button presses to propagate to the Screen? I didn’t work by just changing the type.

_input() has no mode “inheritance” like _process(). You can eventually stop its propagation by calling Viewport::set_input_as_handled() in an event handler whose execution directly precedes unwanted handlers. But you can’t do this in a parent as children’s _input() is called first. You need to do it in a sibling.

So if Overlay is Screen’s sibling, determine if conditions are met in Overlay’s _input() and call set_input_as_handled() there. In that case, the event won’t reach Screen or any of its children.

If you decide to go set_process_input() route, there’s Node::propagate_call() which will spare you from doing iteration/recursion in script code. Or just put relevant nodes into a group and use SceneTree::call_group()

2 Likes

What inputs did it not stop? It typically stops all mouse clicks from propagating down.

What @normalized is suggesting is probably the best route if you’re trying to pause the game without pausing the game. In which case a recursive function to toggle the _input() and _unhandled_input() seems necessary.

I like to make an auto for handling input and use a bunch of signals to send instructions to other nodes. It’s a good way to manage when and how each input gets handled