Attempts at getting 4 player, single camera multiplayer working...

So, I’m having a lot of fun trying to get 4 player single view multiplayer working with UE4 4.0.2

No idea if there is a “correct” way to do it. I’ve tried a bunch of different ways that haven’t worked yet.
My current attempt (which I had working momentarily in Preview mode but has since stopped working)…

Override the GameMode, and create 4 players in the BeginPlay() using:

// Get a reference to my shared camera actor..
AActor *cameraActor = FindActorByName(GetWorld(), TEXT("GameCamera"));

// init the default player
InitPlayer(0, GEngine->GetGamePlayer(GEngine->GameViewport, 0), cameraActor);

// init 3 NEW players (creating their PAWN also)
InitPlayer(1, GEngine->GameViewport->CreatePlayer(1, err, false), cameraActor);
InitPlayer(2, GEngine->GameViewport->CreatePlayer(2, err, false), cameraActor);
InitPlayer(3, GEngine->GameViewport->CreatePlayer(3, err, false), cameraActor);

Inside InitPlayer I move the pawn to one of 4 preset locations so it doesn’t conflict with the spawn position of the next pawn (since there doesn’t seem to be any way to tell CreatePlayer where to spawn the pawn. I also SetViewTarget on the 4 player controllers to the same camera.

APlayerController *controller = player->PlayerController;
	if (controller)
		if (controller->GetPawn())
			FTransform tx = m_playerStarts[playerIdx];
			FVector pos = tx.GetTranslation();
			tx.SetTranslation(pos + FVector(0, 0, 300.0f));

		ASAPlayerState *playerState = (ASAPlayerState*)controller->PlayerState;
		playerState->PlayerIndex = player->ControllerId;

This works perfectly for 3 players in preview mode. Each spawns in its preset location and each is mapped to a different controller. Eventually I’d like to have support for AI characters if there are less than 4 gamepads present, but I’ll cross that bridge later.

I’ve had this working for 4 players, but somehow I’ve broken it and the screen goes black (0,0 canvas size or something) if I CreatePlayer on the 4th player. No idea why yet.

There is also a problem with the PlayerHUDs. Only player #1’s HUD gets its DrawHUD() called - possibly some artifact of how the SetViewTarget are all done to the one camera. I’ve worked around this currently by having the PlayerHUD find the other playerHUD objects and call their draw manually.

A bigger problem is that this all blows up in StandAlone mode because the Engine is creating the player pawns/controllers automatically inside LoadMap, which doesn’t seem to get called for Preview mode and happens AFTER BeginPlay… so it ends up spawning another copy of all the players. But if I tried to take advantage of this, then it wouldn’t work in Preview mode since the player spawns would never get created.

The whole approach seems very hacky.
There are Auto-possess enums on PAWNS but they don’t cause a player & playerController to be created, so not sure how useful they are.
There are the Player_Start nodes, but they also don’t seem to create a second player/playercontroller.

There don’t seem to be any examples or templates that ever try to tackle local multiplayer so I’m not really sure if I’m on the right track yet.
I’ll keep posting to this thread if I make any progress.

Forgot to mention, I’ve overriden the GameViewportClient to disable Splitscreen for that view. This seems to work ok.

void USAGameViewportClient::UpdateActiveSplitscreenType()
	ActiveSplitscreenType = eSST_NONE;

Fixed the problem of standalone-builds created extra pawns by subclassing the LocalPlayer class and overriding the SpawnPlayActor() method so it doesn’t create a controller/pawn if a controller already exists.

bool USALocalPlayer::SpawnPlayActor(const FString& URL, FString& OutError, UWorld* InWorld)
	// already spawned
	if (PlayerController)
		return true;

	return Super::SpawnPlayActor(URL, OutError, InWorld);

Found my problem with the viewport not supporting 4 players. I had rebuilt my project from scratch and forgot to set the default viewport to my subclassed clientViewport.

Since I don’t want to support any splitscreen modes in this game, I’ve overriden the LayoutPlayers to be a little simpler. The way the viewports work, they must do some sort of clear or blit per player, so its important to ensure players 1,2,3 all have 0,0 viewport size and player 0 uses the full viewport area.

void USAGameViewportClient::LayoutPlayers()
	// Initialize the players
	const TArray<ULocalPlayer*>& PlayerList = GetOuterUEngine()->GetGamePlayers(this);
	for (int32 PlayerIdx = 0; PlayerIdx < PlayerList.Num(); PlayerIdx++)
		if (PlayerIdx == 0)
			PlayerList[PlayerIdx]->Size.X = SplitscreenInfo[eSST_NONE].PlayerData[0].SizeX;
			PlayerList[PlayerIdx]->Size.Y = SplitscreenInfo[eSST_NONE].PlayerData[0].SizeY;
			PlayerList[PlayerIdx]->Origin.X = SplitscreenInfo[eSST_NONE].PlayerData[0].OriginX;
			PlayerList[PlayerIdx]->Origin.Y = SplitscreenInfo[eSST_NONE].PlayerData[0].OriginY;
			PlayerList[PlayerIdx]->Size.X = 0.f;
			PlayerList[PlayerIdx]->Size.Y = 0.f;
			PlayerList[PlayerIdx]->Origin.X = 0.f;
			PlayerList[PlayerIdx]->Origin.Y = 0.f;

I think thats all my problems sorted now (until I want to support drop-in/drop-out properly) so I can start moving onto the actual game :slight_smile:

sorry for pumping up this old thread but could you elaborate how you managed to call each HUD? I’ve setup a local multiplayer game without splitscreen as well but only one HUD is drawn. I want each HUD to be drawn. Thanks!