How to intercept all player input?

I’m trying to intercept all key presses (including mouse buttons) for UMG purposes, so I’ve tried overriding the “InputKey” functions in both my GameViewportClient and PlayerController. This works (sort-of), but it seems that when I have a UMG Widget open (e.g. containing a button) and the user clicks on that button, the input is not registered - it seems UMG is intercepting this and it’s not forwarded to either the GameViewportClient or the PlayerController.

I’ve also tried doing this in a custom PlayerInput, but unfortunately “InputKey” is not marked as virtual and hence I cannot override it.
If you’re interested in more specifics:

I’m busy doing action mappings, and I use UMG Buttons to show the key currently mapped to an action.
The player can then click on that button and then I intercept the next key that is pressed and rebind the action to that key. The problem is when a user wants to bind an action to a mouse button and the next click is over the same button. UMG totally intercepts that mouse click and neither the GameViewportClient nor PlayerController ever gets notified of that mouse click.

I would really appreciate some suggestions here.

Thanks!

Also very interested in this, would like to be able to handle all the input myself instead of this auto behavior.

A quick update on this:

I haven’t managed to solve the core issue of intercepting all input, but for UMG I’ve found a workaround.

What I’m doing is that as soon as I want to intercept the user input, I create a new Widget (as a child of the viewport) which contains only a single “Safe Zone” box that covers the whole screen. Then, when the user clicks (or presses a key) the input is rerouted to the Controller/GameViewport and I can notify the widgets from code. When the widget receives the notification I remove the “Safe Zone” widget.

It’s an absolute hack, but it works.

I’m not marking this as an answer as it doesn’t really address the original question, and I’m still interested in doing it properly.

I had also experienced this kind of problem in my project. My solution to this problem is creating a custom FInputModeDataBase :

void FMyCustomInputModeDatabase::ApplyInputMode(FReply& SlateOperations,class UGameViewportClient& GameViewportClient) const
{

	TSharedPtr<SViewport> ViewportWidget = GameViewportClient.GetGameViewportWidget();
	if (ViewportWidget.IsValid())
	{
		SetFocusAndLocking(SlateOperations,WidgetToFocus,bLockMouseToViewport,ViewportWidget.ToSharedRef());

		SlateOperations.ReleaseMouseCapture();

		GameViewportClient.SetIgnoreInput(false);
		GameViewportClient.SetHideCursorDuringCapture(bHideCursorDuringCapture);
		GameViewportClient.SetCaptureMouseOnClick(EMouseCaptureMode::NoCapture);
	}
}

They key here is in the GameViewportClient.SetIgnoreInput(false). If you set this, the InputKey function that you’ve overridden in your PlayerController will still receives inputs even when the widget (the slate application) has already eat them.

Edit: Btw, you can just use the FInputModeGameAndUI that’s already in the Engine, that will pretty much do the same thing as my custom input database, i’ve just remembered that i made it because I also wanted some custom settings in the input mode of my UMG widgets.

This looks great!

Just have one question: How do I “register” this or instruct the input system to use this Input Mode?

Thanks very much for sharing!

You can do that in APlayerController::SetInputMode, it’s callable from blueprint. I usually call it in the Construct event script of the widget (or in the NativeConstruct override if the widget is in C++), I use various kind of InputModes for different kind of widgets in my game.

Ah ok. Fantastic.

Thanks very much Fathurahman.

Thanks for the answer but unfortunately doesn’t solve my problem. This solution at least for me works good for most parts but doesn’t solve the problem completely.

For example if you click an umg button, that click doesn’t get intercepted so PlayerController still doesn’t get it. Seams that “interactable” widgets still consumes the event and doesn’t share it.

Apologies, I accepted that answer before testing properly.

UMG widget’s root is added as a child to the game viewport client, thus all inputs will pass through it before going to the GameViewportClient::InputKey. If an input is handled in a widget, the gameviewportclient will not receive it.

The default UMG button captures the mouse input by returning FReply::Handled(), what you want to do is to make a custom button that returns FReply::Unhandled() even if you actually handles the input so that the input will flow down to the GameViewportClient which will then passed to PlayerController.

Keyboard input is not captured by default, unless you told the widget to do so (by using the slate focus mechanism), the solution is simple, don’t use the focus mechanism (which I think is geared towards desktop application than a multi-platform game).

What i do in my game is, I am mostly using my own custom user widgets (not using UMG button, but my own button widget that doesn’t capture some input keys), i even changed some UMG widget codes to suits my need. I also made my own focusing system. It’s a lot of work but it does the job and i’m quite happy with the result.

UMG is not perfect, but in most cases, it’s a lot better than going full Slate especially with the widget editor. If I was not in a rush to finish my game, i’d probably wait for it to mature a bit more and designed my prototype to work within its limitation.

Thanks! your solution works. But still looking for where the input starts so I can intercept it before going to the UMG widget’s root. Because would like to intercept the input for everything not just buttons so would be great if I don’t need to do custom versions of every widget component.

But anyway thank you :slight_smile: if I don’t find that I will do it this way.

Ive been trying to deal with this too. To solve the UMG eating input like mouse clicks on controls and other things I have done the following:

Made my own User Widget class that inherits from UUserWidget and made sure all my screens use this as their parent.

Implement the two following functions:

virtual FReply NativeOnPreviewKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
virtual FReply NativeOnPreviewMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;

These two are called whenever the widget or a child widget of this user widget are about to get an input call on them. The purpose of these is to intercept and handle the event yourself, which will cause the button or other input widget action to not happen.

You can also use this to just listen to the event for any other purpose but allow the actual widget input to happen by just returning the super result.

Note you could do this in blueprints as well, as this function is a BlueprintImplementableEvent and you could create a parent userwidget blueprint instead and make sure all of your others are reparented to that.

So to make a umg screen which listens for any input, implement the preview commands to handle child widgets taking that input and then make sure you have the user widget as focusable, and then implement the other input events:

OnKeyDown
OnAnalogValueChanged
OnMouseWheel
OnMouseButtonDown
OnMouseMove

That should be enough to get you a rebinding screen.