Simplifying Node Input: The Input Orchestrator Approach
Published on: 2024-05-09
Handling input in Godot isn't overly complicated. You can use the _input
method or the Input class – it really just depends on what you need. There will be times when you want to temporarily disable input for certain nodes while letting others respond. In this post, I'll show you how to achieve that.
In the game I’m building, I'm using what are called game states. A couple of the states I have are PLAYING and PAUSED. When a game state is active, certain UI scenes pop up, and specific input actions are enabled. For example, when the game state PLAYING is active, the score should be visible in the top left corner, player movement should work, and pressing the ESC key triggers the PAUSED state. When the game state PAUSED is active, a pause menu appears, and pressing the ESC key does a couple of things: it closes any open submenus and then returns to the PLAYING state.
To make this work, I’m using something I call an orchestrator. It’s basically a class that keeps track of which nodes should be handling input and which shouldn't. It does this by monitoring the current game state. When a game state is active, the orchestrator enables input for the designated nodes and disables it for everything else. The great thing about this class is that it helps avoid tight coupling and, eventually, lets you configure things without having to touch any code.
There are three main classes involved:
- GameState.gd: This is a simple enum that lists all the available game states.
- GameStateInput.gd: This is a resource we can use right in the Godot editor to set up which nodes should have input processing enabled when a specific game state is active.
- InputOrchestrator.gd: This class keeps track of the current game state, looks for a
GameStateInput
resource that matches the active game state, and then disables input for all nodes except those listed in the foundGameStateInput
.
GameState.gd
class_name Enums
enum GameState {
MENU = 0,
PREPARE = 1,
PLAYING = 2,
PAUSED = 3,
GAME_OVER = 4,
}
GameStateInput.gd
extends Resource
@export var game_state: Enums.GameState
@export var input_nodes: Array[NodePath]
InputOrchestrator.gd
class_name InputOrchestrator
extends Node
@export var game_state_inputs: Array[GameStateInput]
var enabled_input_nodes: Array[NodePath]
func on_game_state_changed(state: int):
_allow_input(state)
func _allow_input(state: Enums.GameState):
enabled_input_nodes = []
for gsi in game_state_inputs:
if state == gsi.game_state:
for input_node in gsi.input_nodes:
var node = get_node(input_node)
print(node.name + " input enabled.")
node.set_process_input(true)
enabled_input_nodes.push_back(input_node)
else:
for input_node in gsi.input_nodes:
if not enabled_input_nodes.has(input_node):
var node = get_node(input_node)
print(node.name + " input disabled.")
node.set_process_input(false)
The InputOrchestratot has an exported variable that lets you configure which nodes should be able to handle input when a specific game state is active. Whenever the game state changes (_on_game_state_changed
), the InputOrchestratot goes through all the configured game states and figures out which ones to enable input for. I'm sure the algorithm could be optimized, but for now, it does the trick.
First things first, you need to attach the InputOrchestratot.gd
script to a node. I usually add them as a direct child of the root node. Once the script is attached, you can start configuring the InputOrchestratot.

Now, you can configure everything in Godot without writing any code. Here's an example of how I've set up my InputOrchestratot.

You can use this to enable or disable input processing for any kind of nodes, not just UI elements. In the image above, I’m using it to enable input for the GameManager and MovementHandler nodes.