_input fires for all events, use for system hotkeys
The _input(event) callback fires for every input event before any GUI processing. Use it for game-wide hotkeys like F5 quicksave or Escape pause menu, and for debug input. Call set_input_as_handled() to prevent further processing.
_unhandled_input fires if GUI didn't consume event
The _unhandled_input(event) callback fires only if no Control consumed the event through _gui_input. Use it for game actions like movement and look. This is the standard choice for gameplay input.
_unhandled_key_input fires for keyboard after GUI
The _unhandled_key_input(event) callback fires for keyboard-only events after GUI processing. Use it for keyboard-specific game actions when you want to ignore gamepad input.
_gui_input fires only when Control is focused or under cursor
The _gui_input(event) callback fires only when a Control has focus or the cursor is over it. Use it for control-specific input like LineEdit text entry. For typing, let LineEdit handle _gui_input and listen to the text_submitted signal instead.
UI overlay input strategy: _input with set_input_as_handled
When a modal overlay (pause menu, inventory) is open, handle input in _input() callback and call set_input_as_handled() to prevent underlying game from seeing the event. This ensures the overlay consumes input and the game-level _unhandled_input is bypassed.
Game action strategy: _unhandled_input with Input Map
For game actions like movement and look, use _unhandled_input() and check Input.is_action_just_pressed() against actions defined in the Input Map. This allows player rebinding and works across keyboard and gamepad.
MOUSE_FILTER_STOP blocks clicks and doesn't propagate
A Control with mouse_filter = MOUSE_FILTER_STOP (0) consumes click events and prevents them from reaching parent nodes or game input handlers. This is the default for buttons. Decorative elements like background ColorRects should use MOUSE_FILTER_IGNORE to prevent blocking clicks.
MOUSE_FILTER_PASS receives click then passes to parent
A Control with mouse_filter = MOUSE_FILTER_PASS (1) receives click events and then passes them up to parent nodes. Use this when a UI element should not block interaction with elements behind it.
MOUSE_FILTER_IGNORE doesn't receive clicks, they pass through
A Control with mouse_filter = MOUSE_FILTER_IGNORE (2) does not receive click events; clicks pass through to parent nodes or game input. Use this for decorative labels, spacers, and background ColorRects to prevent them from blocking input.
Common bug: background ColorRect blocks clicks with default STOP filter
A background ColorRect with the default mouse_filter = MOUSE_FILTER_STOP will consume all clicks and prevent them from reaching buttons or other interactive elements on top. Set decorative backgrounds and labels to mouse_filter = MOUSE_FILTER_IGNORE.
Safe InputEventKey type-safe pattern for 4.6 strict
When handling key events with strict typing, use this pattern: if event is InputEventKey: var ke: InputEventKey = event; if ke.pressed and not ke.echo: match ke.keycode: ...
Use Input Map for rebindable game actions
Game actions like jump, move left, interact should be defined in the Input Map (project.godot under [input] section) and checked with Input.is_action_just_pressed(). This allows players to rebind keys and works consistently across keyboard and gamepad.
Use raw keycodes for system-level hotkeys
System hotkeys like F5 quicksave or F1 help should check raw keycode values directly in _input, not through the Input Map. This prevents players from accidentally rebinding critical system functions.
Echo events occur when key is held, OS sends repeats
When a player holds down a key, the operating system sends repeated input events with event.echo == true. For one-shot actions, check 'not ke.echo' to fire only once per key press.
Echo events are desired for text input to enable key repeat
LineEdit and other text input controls rely on echo events to implement key repeat when a key is held. Do not filter out echo events in text input scenarios.
Modal overlay pattern: grab focus and mark events as handled
When a modal overlay opens, call grab_focus() to capture input. In its _input handler, consume input events with get_viewport().set_input_as_handled() when handling them. This prevents the game-level _unhandled_input from firing.
Gamepad input: use Input Map actions instead of raw buttons
For gamepad compatibility (Steam Deck, console), define game actions in the Input Map with events for both keyboard and gamepad buttons. Then use Input.is_action_just_pressed() which works for both input types automatically.
Gamepad configuration via Steam Input on Steam Deck
On Steam Deck and Steam Deck-like systems, gamepad button remapping and layout configuration is handled by the Steam Input system, not in Godot code. In code, use standard Input Map actions.
_input not firing on Control: check process_mode not DISABLED
If _input callback is not firing on a Control node, verify that process_mode is not set to DISABLED. Disabled nodes and their children do not receive input events.
Click on button does nothing: check disabled flag and parent mouse_filter
When a button click has no effect, verify: (1) button.disabled is not true, (2) parent node mouse_filter is not MOUSE_FILTER_STOP, (3) parent _gui_input is not consuming the event without passing it down.
Keyboard input goes to wrong place: check focus owner and focus_mode
When keyboard input is handled by an unexpected node, use gui_get_focus_owner() to see which node has focus. Verify focus_mode on target nodes and use grab_focus() to redirect input.
Action not in Input Map: check for typo and project.godot
If Input.is_action_just_pressed() never returns true, verify: (1) action name is spelled exactly as in Input Map in project.godot, (2) the action exists under [input] section, (3) action has at least one event defined.
set_input_as_handled stops _unhandled_input chain
Call get_viewport().set_input_as_handled() in _input or _gui_input to mark the event as consumed. This prevents it from reaching _unhandled_input callbacks on the same frame.
accept_event stops _gui_input propagation in containers
Call accept_event() in _gui_input to stop the event from propagating to parent nodes in a Container. Use set_input_as_handled() for scene-level input, not accept_event().