How to setup input for local multiplayer without split-screen

To bind actions and axes in the character blueprint class, I may have multiple such character instances for each new APlayerController. Merely 1 Action Name (e.g. avoiding UseSkill_P1, UseSkill_P2, etc) is required for each character by adding the BindAction nodes in the Player Controller. However, for more functionality, a similar implementation is necessary in cpp.

There exist similar questions, but they are either: not specific, outdated, or lack answers. With this question, I am hoping to provide a good reference point for anyone wanting to setup a local multiplayer (couch co-op) game.

What I've tried:

When I bind actions and axes (delegates) in the APlayerController's SetupInputComponent (e.g: InputComponent->BindAction("Attack", IE_Pressed, this, &AMyController::DoSomething);, then spawn multiple players with UGameInstance::CreateLocalPlayer(index, err, true), the editor will crash on pressing Play in Editor (Alt+P).

The error thrown during runtime is: Access violation - code c0000005 (first/second chance not available) and referencing the line where the first ActionBind appeared, e.g: UE4Editor_MyProject!AMyController::SetupInputComponent() [d:\path\to\project\source\private\mycontroller.cpp:66]

A common solution can be found in this wiki post it seems. In short, overwrite GameViewportClient and set it as default, then within, overwrite InputAxis and InputKey and increment the player controller index for each input device (gamepad) used.

As mentioned previously, this would require the project to specify a unique Action Name for each player index e.g. Attack_P4.

There’s are also some points worth mentioning:

  • An AController will not be created for each input device connected.
  • 1-viewport-multiplayer isn’t supported as split-screen. (Weird naming, but good for search engines).
  • Spawning new Local Players is generally done in your override of AGameMode::BeginPlay. I made it up to the deriving BPs of my Game Mode to call CreatePlayers to create up to the set amount of players.
  • Setting Skip Assigning Gamepad to Player 1 will allow keyboard to be used for player 1.

Hopefully there are some common best practices to tackle this problem. Any suggestion is very much appreciated!

PS Here is the related gamedev question.

I tried the template approach, where it overrides SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) in the ACharacter derived class, and then bind delegates by calling e.g: PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);

For some reason this reminded me to include Super::SetupInputComponent(); in my APlayerController::SetupInputComponent and the bindings started working. The editor wouldn’t crash any longer.

The first device of any type is bound to Player 0 - so the Keyboard, Mouse, first Gamepad etc.

You’ve already seen the solution to increment the Gamepad (so you can have one player on Keyboard, second on Gamepad and so forth). I added a simple boolean flag to my project, so that if there are 4x gamepads, then all players can use them. Whereas if players want 4 player, but only 3 pads and a keyboard, you can quickly switch the flag.

Now, what you’ve said with the Player Controller needing different function binds to the Character… I think there’s something wrong with your setup.

For every player that joins, a Player Controller will be created. If you’ve set up your default Player Controller and Default Pawn, in your projects Maps and Modes (or overwritten them in your map) then each PC will control each Pawn (Character). They’ll know exactly which one they’re connecting to. If you use “Get Controlled Pawn” within the controller, when passing on a function, you can then cast to your Pawn/Character and call the appropriate methods.

If you use “PlayerStart” (either in one location, or 4 locations, if you want strict offsets for all players) then each Pawn will spawn at one of the PlayerStarts. I wanted strict spawn locations, so I know that there are 4x PlayerStart’s, and I set them accordingly.

Lastly, if you’re not doing Split Screen, you might need to overwrite the Player Camera Manager, in the Player Controller, so that it points to a shared camera… that’s also what I’ve had to do.