Top Down Local Multiplayer with 4 Players and only 2 Camera Splitscreen

Hey Guys, i am struggeling with following Situation. I want to create a top-down-bee-adventure game with a local multiplayer up to 4 players. I wish to have a Splitscreen with two views. One shows the inside of de bee hive and the other shows the outside like in the Picture below.


The players can allready move between thes two places with portals.
The Problem: If i enable Use Splitscreen in die Project Settings there are 4 views, each for every Player.

I use Player Start Components witch creates automaticly a Camera for all of my Players, and i have no controll about that. And i dont found a option to select the cameraviews i want in my splitscreen. Hope somebody can help.

Hey there @GuteNudel25! Welcome back to the community! A bee pollination overcooked style game sounds really cool!

So this is a bit of a common issue because the split screen system isn’t really exposed to external scripts too much. The logic was that tying it to the camera system would cause complexity issues. The side effect is that there’s little control over how the splitscreen cameras without exposing the system itself in the source, which is a bit more complicated than it sounds!

Most methods of workarounds are decently impractical, like routing all player inputs through 2 handlers or using render targets and just having 1 camera with a plane for the second location cover precisely half of the screen, which I understand isn’t ideal.

Disclaimer: One or more of these links are unaffiliated with Epic Games. Epic Games is not liable for anything that may occur outside of this Unreal Engine domain. Please exercise your best judgment when following links outside of the forums.

Render target/ scene capture 2D tutorial:

Hey @SupportiveEntity, thank you for the fast reply.

I allready implementet a HUD with one view as Render Target. But this costs me 15fps and my Camera movement System dont work verry nice, because the main cameraview is still across the hole screen.

What do you mean with [quote=“SupportiveEntity, post:2, topic:684725”]
outing all player inputs through 2 handlers
[/quote] ?
You know a toturial for this method, too?

Yeah it’s definitely not ideal to run the RT, the best method would be to dig directly into the source and expose some of the split screen logic, but I’m not well versed enough at working with the source to be able to work that out on the fly.

For the other method, I was trying to find the thread that I had read on it before but couldn’t come up with it. So I’ll do a bit of digging to see if we can get those inputs without a player controller present.

I also thinking about unsing the ndisplay plugin, but dont know if this plugin works for my purpose. The Game is for a beekeeper museum, so instead of 1 monitor, i can use two monitor as well, if the plugin only works will multiple monitors and no splitscreens.

I’m not too knowledgeable about nDisplay to be able to recommend it for this purpose. Glancing over some it’s documentation it seems like you’d be able to utilize multiple cameras without split screen. However it does seem like the solution will be significantly more complex. I couldn’t figure out a way to pull it off without player controllers being attached. The alternative is editing the source a bit, which also could be a deeper challenge than just not generating more viewports past 2.

Hi, i made a change in GameViewportClient.h.
image
But changing this value from 4 to 2 has no effect. Do you know why? I created a backupfile and tried restart the engine after change in visual studio and save. Do i have to note something else when editing engine code?

Engine code needs to be recompiled in the source version of unreal, if you’re editing it directly in the launcher version it won’t do anything outright. If you are working in source and recompiled then I’d look over the header and CPP file for it and make sure how that variable is used. I assume you already have but I always have to mention it.

So finally i have made it. I followed the instuctions of how to change unreal source code with this video → How To EDIT Unreal's Source (Engine) Code! | How To Make YOUR OWN Game | UE4/UE5 & C++ Tips & Tricks - YouTube. To mension is, that you need a lot of free memory (194GB). But changing the int32 MaxSplitscreenPlayers to 2 is not the clue. It have to be 4, otherwise there are only 2 playercontroller. Instead of this value i changed in the UGameViewportClient::UpdateActiveSplitscreenType() method case 4. I just pasted anything from case 2 in.


Build the Engine (may take 1-3h) and this is the end result:

performant and without any z buffer issues. Thanks for your help!

1 Like

Amazing work! There’s a kind of “Creator Spotlight”/“Cool things on the forums” thing we do, would you mind if I submit your post here for that? It might just bring some (possibly unwanted) attention or nothing at all depending on who sees it.

Thank you! Sure, no problem.

1 Like

It would be better to inherit from UGameViewportClient instead of editing the original engine code. You don’t need to compile the engine from source and hinder future split screen projects.

Example:
.h

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameViewportClient.h"
#include "GameViewportClientSplit.generated.h"

/**
 * 
 */
UCLASS()
class YOUR_API UGameViewportClientSplit : public UGameViewportClient
{
	GENERATED_BODY()

public:
		virtual void UpdateActiveSplitscreenType() override;
	
		/** Allows game code to disable splitscreen (useful when in menus) */
		void SetForceDisableSplitscreen(const bool bDisabled);

private:
	bool bDisableSplitScreenOverride;

};

cpp

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


#include "GameViewportClientSplit.h"
#include "UObject/Class.h" 
#include "GameMapsSettings.h"

void UGameViewportClientSplit::UpdateActiveSplitscreenType()
{
	
	ESplitScreenType::Type SplitType = ESplitScreenType::None;
	const int32 NumPlayers = GEngine->GetNumGamePlayers(GetWorld());
	
	const UGameMapsSettings* Settings = GetDefault<UGameMapsSettings>();
	
	if (Settings->bUseSplitscreen && !bDisableSplitScreenOverride)
	{
		switch (NumPlayers)
		{
		case 0:
		case 1:
			SplitType = ESplitScreenType::None;
			break;

		case 2:
			switch (Settings->TwoPlayerSplitscreenLayout)
			{
			case ETwoPlayerSplitScreenType::Horizontal:
				SplitType = ESplitScreenType::TwoPlayer_Horizontal;
				break;

			case ETwoPlayerSplitScreenType::Vertical:
				SplitType = ESplitScreenType::TwoPlayer_Vertical;
				break;

			default:
				check(0);
			}
			break;

		case 3:
			switch (Settings->ThreePlayerSplitscreenLayout)
			{
			case EThreePlayerSplitScreenType::FavorTop:
				SplitType = ESplitScreenType::ThreePlayer_FavorTop;
				break;

			case EThreePlayerSplitScreenType::FavorBottom:
				SplitType = ESplitScreenType::ThreePlayer_FavorBottom;
				break;

			case EThreePlayerSplitScreenType::Vertical:
				SplitType = ESplitScreenType::ThreePlayer_Vertical;
				break;

			case EThreePlayerSplitScreenType::Horizontal:
				SplitType = ESplitScreenType::ThreePlayer_Horizontal;
				break;

			default:
				check(0);
			}
			break;

		case 4:
			switch (Settings->TwoPlayerSplitscreenLayout)
			{
			case ETwoPlayerSplitScreenType::Horizontal:
				SplitType = ESplitScreenType::TwoPlayer_Horizontal;
				break;

			case ETwoPlayerSplitScreenType::Vertical:
				SplitType = ESplitScreenType::TwoPlayer_Vertical;
				break;

			default:
				check(0);
			}
			break;


		default:
			
			ensure(NumPlayers == 4);
			switch (Settings->FourPlayerSplitscreenLayout)
			{
			case EFourPlayerSplitScreenType::Grid:
				SplitType = ESplitScreenType::FourPlayer_Grid;
				break;

			case EFourPlayerSplitScreenType::Vertical:
				SplitType = ESplitScreenType::FourPlayer_Vertical;
				break;

			case EFourPlayerSplitScreenType::Horizontal:
				SplitType = ESplitScreenType::FourPlayer_Horizontal;
				break;
			
			default:
				check(0);
			}
			break;
		}
	}
	else
	{
		SplitType = ESplitScreenType::None;
	}
	ActiveSplitscreenType = SplitType;	
}

void UGameViewportClientSplit::SetForceDisableSplitscreen(const bool bDisabled)
{
	bDisableSplitScreenOverride = bDisabled;
	LayoutPlayers();
}

And then just set it in your project

Edit: forgot to mention you need to add the “EngineSettings” module in your .build file otherwise you will get compile errors!

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EngineSettings" });
``
2 Likes

oh nice. That would have saved me a lot of time and memory. I will definitely try it. Thanks!