Hello there!
After searching a lot, I finally found a solution by talking to MordenTral (awesome dude and author of two amazing plugins). Basically, the clean solution to achieve this is to create a child class of UGameViewportClient which overrides InputKey(…) and InputAxis(…). In the overrides, you can reroute where certain inputs go and both functions have bool bGamepad as a parameter so you can basically use that to determine where you want to send the input. It is necessary to set the Game Viewport Client Class in Project Settings to be the newly created class. You can make this class blueprintable and expose an Enum UPROPERTY to Blueprint so that you can actually create a child blueprint class and can change behavior dynamically at runtime using blueprints (of course the Game Viewport Client Class in Project Settings would need to point to this blueprint class).
Here is my UCustomGameViewportClient class:
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameViewportClient.h"
#include "CustomGameViewportClient.generated.h"
/** This class introduces a new Input-PlayerController binding behaviour. Through the EGameInputMethod Enum it is possible to choose the default
* behavior (in which keyboard/mouse input and gamepad input are both binded to Player 1 [index 0]) or the Player1OnlyKeyboardAndMouse behavior
* (in which keyboard/mouse input is binded to Player 1 [index 0] and gamepad input is shifted to the other players).
*/
UENUM(Blueprintable)
enum class EGameInputMethod : uint8
{
GameInput_Default,
GameInput_Player1OnlyKeyboardAndMouse,
};
UCLASS(Blueprintable)
class SIDESCROLLER_API UCustomGameViewportClient : public UGameViewportClient
{
GENERATED_BODY()
public:
// Input Method for the viewport
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Input")
EGameInputMethod GameInputMethod;
virtual bool InputKey(FViewport* tViewport, int32 ControllerId, FKey Key, EInputEvent EventType, float AmountDepressed = 1.f, bool bGamepad = false) override
{
if (GameInputMethod == EGameInputMethod::GameInput_Default)
return Super::InputKey(tViewport, ControllerId, Key, EventType, AmountDepressed, bGamepad);
if (GameInputMethod == EGameInputMethod::GameInput_Player1OnlyKeyboardAndMouse && bGamepad)
{
// shift gamepad input to controllers with +1 index
++ControllerId;
return Super::InputKey(tViewport, ControllerId, Key, EventType, AmountDepressed, bGamepad);
}
else
{
return Super::InputKey(tViewport, ControllerId, Key, EventType, AmountDepressed, bGamepad);
}
}
virtual bool InputAxis(FViewport* tViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples = 1, bool bGamepad = false) override
{
if (GameInputMethod == EGameInputMethod::GameInput_Default)
return Super::InputAxis(tViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad);
if (GameInputMethod == EGameInputMethod::GameInput_Player1OnlyKeyboardAndMouse && bGamepad)
{
// shift gamepad input to controllers with +1 index
++ControllerId;
return Super::InputAxis(tViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad);
}
else
{
return Super::InputAxis(tViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad);
}
}
};
Create Blueprint class from this, and you’ll see ou can change the Enum to whatever you like.
To change this at runtime you’ll need to access this class with UWorld::GetGameViewport(), so make a function in a c++ blueprint function library (if you want to have access via Blueprints):
class UGameViewportClient* FunctionLibrary::GetGameViewport(const UObject* WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
return World ? World->GetGameViewport() : nullptr;
}
Once you have that exposed, all you need to do is cast it to your custom Game Viewport class and set the enum to the desired value.
I must say that this works perfectly for me… but I also have input on UI and in that case it isn’t working, I still need to find out what happens in that case.