Split Screen with over 4 players

Hi, I’ve wanted to have my game support split screen for more than 4 players, but it has a hard code cap on 4, and when you remove that cap, there are still problems.
The only way I’ve found that does this is changing the engine source code, and game code a little bit.

I’m putting this here to make life easier for whoever wants to do this as well.

First, in your engine project, go to ViewportSplitScreen.h, and add another value to the ESplitScreenType enum, For this tutorial, we’ll add EightPlayer to it.

Then, go to the constructor of GameViewportClient.h, and notice this code.



	SplitscreenInfo[ESplitScreenType::FourPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.5f, 0.5f, 0.0f, 0.0f));
	SplitscreenInfo[ESplitScreenType::FourPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.5f, 0.5f, 0.5f, 0.0f));
	SplitscreenInfo[ESplitScreenType::FourPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.5f, 0.5f, 0.0f, 0.5f));
	SplitscreenInfo[ESplitScreenType::FourPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.5f, 0.5f, 0.5f, 0.5f));

This is the actual split of the split screen players on the screen. So you need to add one for your amount of players:

For example, this is one split that you could have for 8 players




//Top Row
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.0f, 0.0f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.25f, 0.0f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.50f, 0.0f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.75f, 0.0f));

//Bottom Row
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.0f, 0.5f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.25f, 0.5f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.50f, 0.5f));
	SplitscreenInfo[ESplitScreenType::EightPlayer].PlayerData.Add(FPerPlayerSplitscreenData(0.25f, 0.5f, 0.75f, 0.5f));

Add those according to whatever split screen split you need.
Then, in the same constructor, find the line


MaxSplitscreenPlayers=4;

and set it to your maximum split screen players.

EDIT: forgot a step:

Now go to UGameViewportClient::UpdateActiveSplitscreenType() and add a case for your amount of players
For example, I would add



		case 8:
			SplitType = ESplitScreenType::EightPlayer;
			break;


Now, there is one last thing you must do for it to not crash.
Go to IndirectLightingCache.cpp, to FIndirectLightingCache::UpdateCachePrimitivesInternal, and change line 498



TArray<uint32> SetBitIndices[4];


to



TArray<uint32> SetBitIndices[8];



or whatever you want your maximum split screen players to be, otherwise your engine will crash.

After that compile your engine and project, and it should work.

idk,before with a slight modification i was able to get 16 person splitscreen

Pics of it working?

Edited, forgot a step on the tutorial

Yeah, I’ll post a pic on sunday when I’m able to

Forgive the thread revival, but I’m tearing hairs out trying to get more than 4 player local…mostly probably due to my lack of knowledge on how to use C++ with UE4.

I’ve tried to create a child C++ class based on GameViewPortClient from within the editor, but I am not sure I can simply create a child class, set the variable, then change the default viewport class in the editor to my new GameViewPortClient class.

Do I need to get the source from GitHub in order to change the GameViewPortClient directly to remove the hard lock?

Yes, you should get code from github and make appropriate changes. Today I tried and it Worked great! Here is a 5 player vertical split. Thanks to author!

Can someone compile this for the latest 4.20 release and share it please.

Hi Everyone,

I wonder if this can be done by overriding the code in the custom Cpp class that inherits the GameViewportClient, instead of editing the Engine Source? Did anyone try this?

It really works:D

UPDATE (Check bottom of post)

I did try, but had no success.
It could be perhaps that I did it wrong or something, but I don’t think it’s really hard to get it wrong as it’s literally just overriding a variable.
I also tried to create a blueprint node that would override the maximum number of players, this also didn’t work.
When I say “It didn’t work” I mean that I try to “Create Player” 8 times and check to see if the controller is valid, and 3 out of 8 are valid, with 5 GamePads connected to the computer, all working and tested within WMInputManager plugin.
However pawn number 5 cannot be possessed as there is no controller to posses the pawn.

I did this as other forum posts have said that this is all that needs to be done, but it just doesn’t work for me, but as I said, it could be due to me doing it wrong.
My next step will be figuring out how to use the github source. If this is the only way it can be done, then that’s how it’s gonna get done.

This is what my EightPlayerGameViewportClient child class looks like.


// Fill out your copyright notice in the Description page of Project Settings.

#include "EightPlayerGameInstance.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Engine/LocalPlayer.h"

ULocalPlayer* UEightPlayerGameInstance::RequestLocalPlayer(int32 ControllerId, FString& OutError, bool bSpawnActor)
{
    check(GetEngine()->LocalPlayerClass != NULL);

    ULocalPlayer* NewPlayer = NULL;
    int32 InsertIndex = INDEX_NONE;

    const int32 MaxSplitscreenPlayers = 8; override // This is what does the trick!

    if (FindLocalPlayerFromControllerId(ControllerId) != NULL)
    {
        OutError = FString::Printf(TEXT("A local player already exists for controller ID %d,"), ControllerId);
    }
    else if (LocalPlayers.Num() < MaxSplitscreenPlayers)
    {
        // If the controller ID is not specified then find the first available
        if (ControllerId < 0)
        {
            for (ControllerId = 0; ControllerId < MaxSplitscreenPlayers; ++ControllerId)
            {
                if (FindLocalPlayerFromControllerId(ControllerId) == NULL)
                {
                    break;
                }
            }
            check(ControllerId < MaxSplitscreenPlayers);
        }
        else if (ControllerId >= MaxSplitscreenPlayers)
        {
            UE_LOG(LogPlayerManagement, Warning, TEXT("Controller ID (%d) is unlikely to map to any physical device, so this player will not receive input"), ControllerId);
        }

        NewPlayer = NewObject(GetEngine(), GetEngine()->LocalPlayerClass);
        InsertIndex = AddLocalPlayer(NewPlayer, ControllerId);
        if (bSpawnActor && InsertIndex != INDEX_NONE && GetWorld() != NULL)
        {
            if (GetWorld()->GetNetMode() != NM_Client)
            {
                // server; spawn a new PlayerController immediately
                if (!NewPlayer->SpawnPlayActor("", OutError, GetWorld()))
                {
                    RemoveLocalPlayer(NewPlayer);
                    NewPlayer = NULL;
                }
            }
            else
            {
                // client; ask the server to let the new player join
                NewPlayer->SendSplitJoin();
            }
        }
    }
    else
    {
        OutError = FString::Printf(TEXT("Maximum number of players (%d) already created.  Unable to create more."), MaxSplitscreenPlayers);
    }

    if (OutError != TEXT(""))
    {
        UE_LOG(LogPlayerManagement, Log, TEXT("UPlayer* creation failed with error: %s"), *OutError);
    }

    return NewPlayer;
}




UPDATE: 18th Jan 19
I can confirm after extensive testing that you DO need to use the GitHub source code to remove the 8 player hard cap.
However pingvincible has 5 player splitscreen there, I do wonder if you can even control that 5th player, and if so, how? (if you aren’t using plugins).

I am using WMInputManager which also allows you to assign Gamepads to controllers and remembers it even after leaving the editor as it’s stored in save files. It also lets you choose which controllers to treat as HID or generic gamepads, which in my case, I have 8 Xbox One wireless controllers running of the xbox wireless adapter, so this allowed me to change controllers 5-8 to generic gamepads, although I don’t think it was necessary.
Also in the project settings, I added HID inputs (WMInputManager allows you to select HID inputs) to my input actions, so for example HID Generic Gamepad Face Button DOWN is the equivallent of Gamepad Face Button DOWN.

I managed to finally convert my project over to use the source from github, and 8 players successfully worked :slight_smile: I believe it could even work with more than 8, but I think 8 players for a mario party style game is more than chaotic enough :wink:

Thanks pingvincible for convincing me to use github source, this is a huge milestone for my game, as simple as it may seem, and helps it stand out from the crowd too for when it is complete.

please updata UE4.21

This is really great thanks :slight_smile:

I’ve added a 4.24 branch in this github fork which adds splitscreen layouts for 5-8 players. All the changes are in the topmost commit of the branch.

Dynamic shadows were missing in viewports 5-8 when I was first testing, but I then discovered rebuilding lighting for the level fixes it.

Just FYI you don’t need to do engine modifications to get more than 4 viewports or to modify layouts. This can be done with a small C++ function library.

I’ve implemented a SplitScreenManager actor rather than a function library, and I am setting up 5 viewports (three columns, two rows, with one blank space). My code looks like this, which should be enough to get anyone else trying to do this started.



#include "Engine/World.h"
#include "Engine/GameViewportClient.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/LocalPlayer.h"

...

void ASplitScreenManager::ApplyFiveViewportSettings()
{
    UGameViewportClient* Viewport = GetWorld()->GetGameViewport();
    Viewport->MaxSplitscreenPlayers = 5;

    FSplitscreenData ScreenLayout;

    float W = 1.0f / 3.0f;
    float H = 1.0f / 2.0f;

    auto Screen1 = FPerPlayerSplitscreenData(W, H, 0 * W, 0 * H);
    auto Screen2 = FPerPlayerSplitscreenData(W, H, 1 * W, 1 * H);
    auto Screen3 = FPerPlayerSplitscreenData(W, H, 1 * W, 0 * H);
    auto Screen4 = FPerPlayerSplitscreenData(W, H, 2 * W, 1 * H);
    auto Screen5 = FPerPlayerSplitscreenData(W, H, 2 * W, 0 * H);

    ScreenLayout.PlayerData.Add(Screen3);
    ScreenLayout.PlayerData.Add(Screen4);
    ScreenLayout.PlayerData.Add(Screen5);
    ScreenLayout.PlayerData.Add(Screen2);
    ScreenLayout.PlayerData.Add(Screen1);

    Viewport->SetDisableSplitscreenOverride(true);
    Viewport->SplitscreenInfo[ESplitScreenType::None] = ScreenLayout;
}


The important things seem to be:

  • Setting the GameViewport MaxSplitsceenPlayers (to ensure you’re allowed to spawn more than 4 players)
  • Setting DisableSplitscreenOverride to true (counterintuative, but this ensures the ESplitScreenType mode is forced to None)
  • Setting the viewport SplitscreenInfo for ESplitScreenType::None to your desired layout