I’ve found the solution that works for now and I’m posting an explanation here for everyone to use it and build upon.
I’ve subclassed AGameMode
, since I do all my player management there, and put this in BeginPlay
:
OnControllerConnectionHandle = FCoreDelegates::OnControllerConnectionChange.AddUFunction(this, FName("OnControllerConnectionChange"));
with OnControllerConnectionChange
being declared like this:
UFUNCTION(BlueprintImplementableEvent, Category = "Input", Meta = (DisplayName = "On Controller Connection Change"))
void OnControllerConnectionChange(bool Connected, int32 UserID, int32 ControllerID);
Now, OnControllerConnectionChange
fires every time a gamepad or any other controller recognized by Windows is plugged in or pulled out.
Note: UserID
is pretty much useless right now, as it always returns -1, but ControllerID
will always return the index of the controller, starting from 0. If you plug the second gamepad, it will have index == 1, and if you pull out the first one after that, the index of the second won’t change, so you can (almost) safely tie ControllerID
to PlayerController
index.
Another thing - editor is completely unreliable when testing this. First of all, the event fires for every controller plugged on the game launch, so you can actually launch splitscreen right on or safely tie ControllerID
to player indices, but only in Standalone mode or packaged builds, it doesn’t in the editor. Second, pulling out/plugging in during PIE fires the event 3 (three) times. Third, and it really grinds my gears - sometimes pulling out/plugging in silently crashes/closes the editor.
Don’t forget to call Remove
for OnControllerConnectionHandle
on EndPlay
so you don’t crash anything:
FCoreDelegates::OnControllerConnectionChange.Remove(OnControllerConnectionHandle);
I’m still working on exposing input events from second gamepad without creating PlayerController
specifically for this.
(You should also check out FCoreDelegates
, it has lots of interesting stuff not exposed to blueprints.)