Input events and UI
Choose an input callback, let UI handle events first and stop propagation when consumed.
Where to handle input
| Task | Location |
|---|---|
| Continuous movement or analog stick strength | Input in _PhysicsProcess() |
| Gameplay action after UI | _UnhandledInput(InputEvent) |
| Keyboard events after UI | _UnhandledKeyInput(InputEvent) |
| Custom button or UI region | Control._GuiInput(InputEvent) |
| Interception before UI | _Input(InputEvent); only when necessary |
Within a Viewport, an event passes through _Input() and then eligible UI controls. If it remains unhandled, _ShortcutInput(), _UnhandledKeyInput() for keys, and _UnhandledInput() follow. This is a simplified order: embedded windows, nested viewports and physics object picking add stages. Marking an event handled stops its later stages.
An action after UI
Add interact in Project Settings → Input Map and bind E. Attach InteractReceiver.cs to an ordinary entity in the game level:
using Reckfell;
public partial class InteractReceiver : Entity
{
public int Interactions { get; private set; }
public override void _UnhandledInput(InputEvent @event)
{
if (!@event.IsActionPressed("interact"))
return;
Interactions++;
ReckfellApi.Print("Interaction ", Interactions);
GetViewport().SetInputAsHandled();
}
}Build and run. Pressing E increments the counter once if UI did not consume the event. InputEvent.IsActionPressed() defaults to allowEcho = false, so held-key echoes do not count as new presses. SetInputAsHandled() prevents later recipients from handling this event.
Inside Control._GuiInput(), call AcceptEvent() for a consumed UI event. Mouse input depends on control size, MouseFilter, overlap order and pointer position; keyboard input depends on focus. MouseFilter.Stop automatically stops pointer events at the Control even without explicit AcceptEvent(); scrolling has a separate parent-propagation rule.
Handled events do not block Input polling
Input.IsActionPressed() reads global device state, not a list of unhandled events. A press accepted by a menu can still affect gameplay polling. Disable gameplay controls explicitly or pause their branch.
Input.ActionPress() changes an action's polled state but does not dispatch an event to _Input(). Use Input.ParseInputEvent() for a synthetic event or PushInput() for an isolated Viewport. Synthetic input does not replace testing a real device.
Find the cause
| Symptom | Check |
|---|---|
| One press triggers two actions | The same logic in _Input() and _UnhandledInput(), two active listeners or duplicate subscriptions. Keep one owner of the gameplay action; do not manually call both handlers. |
A key never reaches _UnhandledInput() | The interact Input Map binding and the keyboard-focused Control: UI may have consumed the event already. Observe _Input() for diagnosis rather than moving gameplay there to bypass menus. |
| A button under an image never receives clicks | Overlapping Control bounds and order. Set MouseFilter.Ignore on a purely decorative layer; do not apply it indiscriminately to interactive UI. |
| The character moves through an open menu | Gameplay polls Input, which ignores event consumption. Explicitly disable gameplay controls or pause their branch when opening the menu. |
Keep discrete events distinct from holding: @event.IsActionPressed() suits one action; Input.IsActionPressed() reads held state.