Download

Differen GameModes / PlayerContollers?

Hey Guys,

I am pretty new to the Unreal Engine and just got started with my first project. My game is a OneVSOne Multiplayer Game.
One Player has a Diablo 3 like perspektive and can do the usual Diablo-style stuff like walking, attacking…
The other player plays the environment. He has a RTS-Top Down perspective and controls all the monsters on the map ( like a RTS game).
Its basically two different game types playing together. What would be the best way to seperate the different game logics?
From my understanding I can only have one game type if they both wanna play together. So I would need two different playerControllers ( one for the RTS Game Player and one for the RPG Game player)
Any thoughts, suggestions?

Cheers Choba

I am doing something similar for a project I am working on. I use two different player controller types for the two different player types. My game mode class is where I put the logic to decide which player controller type to spawn. I had a setup like this in the UDK and it worked out well. I’ve be porting my code over to UE4 and so far everything seems to be working out. I can provide more details/info if you need some more direction.

Joshua

Hi,

thanks for the answer. I was thinking in the same direction. I know this is probably asking a lot, but is there a chance to have a look at your code?

Cheers Choba

If Epic can share their code with everyone, I can do the same as well. :slight_smile:

I handle it by overriding three different methods in my Game Mode class.

Here’s the header for for my Game Mode class:

EliminationGameMode.h


#pragma once

#include "GameFramework/GameMode.h"
#include "EliminationGameMode.generated.h"

/**
 * 
 */
UCLASS()
class AEliminationGameMode : public AGameMode
{
	GENERATED_UCLASS_BODY()

	virtual void BeginPlay() OVERRIDE;

	virtual APlayerController* Login(const FString& portal, const FString& options, const TSharedPtr<class FUniqueNetId>& uniqueId, FString& errorMessage) OVERRIDE;

	virtual void PostLogin(APlayerController* newPlayer) OVERRIDE;

	virtual APlayerController* SpawnPlayerController(FVector const& SpawnLocation, FRotator const& SpawnRotation) OVERRIDE;

	virtual void GenericPlayerInitialization(AController* C) OVERRIDE;

	// Selects the best spawn point for the player.
	virtual AActor* ChoosePlayerStart(AController* player) OVERRIDE;

	virtual void RestartPlayer(AController* newPlayer) OVERRIDE;

	// Initialize the GameState actor.
	virtual void InitGameState() OVERRIDE;

	virtual UClass* GetDefaultPawnClassForController(AController* inController) OVERRIDE;

protected:
	// Check if player can use spawnpoint.
	virtual bool IsSpawnpointAllowed(APlayerStart* SpawnPoint, AController* Player) const;

	// Check if player should use spawnpoint.
	virtual bool IsSpawnpointPreferred(APlayerStart* SpawnPoint, AController* Player) const;

	APlayerController* SpawnPlayerController(FVector const& SpawnLocation, FRotator const& SpawnRotation, TSubclassOf<APlayerController> pcClass);

	TSubclassOf<class APawn> MarinePawnClass;
	TSubclassOf<class APawn> BaseSpectatorPawnClass;
};

In my constructor, I grab the blueprints for the two different pawns that will be spawned.

Constructor:


AEliminationGameMode::AEliminationGameMode(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	//Set the default pawn to a blueprinted pawn.
	static ConstructorHelpers::FObjectFinder<UBlueprint> playerPawnObject(TEXT("Blueprint'/Game/Blueprints/Pawns/BP_MarineCharacter.BP_MarineCharacter'"));
	if (playerPawnObject.Object != NULL)
	{
		MarinePawnClass = (UClass*)playerPawnObject.Object->GeneratedClass;
	}

	//Get the blueprint base spectator pawn.
	static ConstructorHelpers::FObjectFinder<UBlueprint> spectatorPawn(TEXT("Blueprint'/Game/Blueprints/BugPlayer/BP_BaseSpectatorPawn.BP_BaseSpectatorPawn'"));
	if (spectatorPawn.Object != NULL)
	{
		BaseSpectatorPawnClass = (UClass*)spectatorPawn.Object->GeneratedClass;
	}

	GameStateClass = ABaseGameState::StaticClass();
	SpectatorClass = ABaseSpectatorPawn::StaticClass();
}

I override the Login method so I can call my custom SpawnPlayerController. My implementation is the same as the base class except for one line (See red text below).

Login:


APlayerController* AEliminationGameMode::Login(const FString& portal, const FString& options, const TSharedPtr<class FUniqueNetId>& uniqueId, FString& errorMessage)
{
	ABaseGameState* const gameState = GetGameState<ABaseGameState>();
	TSubclassOf<APlayerController> newPlayerControllerClass;

	errorMessage = GameSession->ApproveLogin(options);
	if (!errorMessage.IsEmpty())
	{
		return NULL;
	}

	if (gameState == NULL)
	{
		return NULL;
	}

	// Spawn the Bug Player first.
	// TODO: Find out why the Marine Player can't spawn first.  Bug player doesn't spawn correctly when the Marine Player spawns first.
	if (gameState->BugPlayer == NULL)
	{
		newPlayerControllerClass = ABugPlayerController::StaticClass();
	}
	else
	{
		newPlayerControllerClass = AMarinePlayerController::StaticClass();
	}

	APlayerController* newPlayer = SpawnPlayerController(FVector::ZeroVector, FRotator::ZeroRotator, newPlayerControllerClass);

	// Handle spawn failure.
	if (newPlayer == NULL)
	{
		//UE_LOG(LogGameMode, Log, TEXT("Couldn't spawn player controller of class %s"), PlayerControllerClass ? *PlayerControllerClass->GetName() : TEXT("NULL"));
		errorMessage = FString::Printf(TEXT("Failed to spawn player controller"));
		return NULL;
	}

	// Customize incoming player based on URL options
	InitNewPlayer(newPlayer, uniqueId, options);

	// Find a start spot.
	AActor* const StartSpot = FindPlayerStart(newPlayer, portal);
	if (StartSpot == NULL)
	{
		errorMessage = FString::Printf(TEXT("Failed to find PlayerStart"));
		return NULL;
	}

	FRotator InitialControllerRot = StartSpot->GetActorRotation();
	InitialControllerRot.Roll = 0.f;
	newPlayer->SetInitialLocationAndRotation(StartSpot->GetActorLocation(), InitialControllerRot);
	newPlayer->StartSpot = StartSpot;

	// Register the player with the session
	GameSession->RegisterPlayer(newPlayer, uniqueId, HasOption(options, TEXT("bIsFromInvite")));

	// Init player's name
	FString InName = ParseOption(options, TEXT("Name")).Left(20);
	if (InName.IsEmpty())
	{
		InName = FString::Printf(TEXT("%s%i"), *DefaultPlayerName, newPlayer->PlayerState->PlayerId);
	}
	ChangeName(newPlayer, InName, false);

	// Set up spectating
	bool bSpectator = FCString::Stricmp(*ParseOption(options, TEXT("SpectatorOnly")), TEXT("1")) == 0;
	if (bSpectator || MustSpectate(newPlayer))
	{
		newPlayer->StartSpectatingOnly();
		return newPlayer;
	}

	if (bDelayedStart)
	{
		newPlayer->bPlayerIsWaiting = true;
		newPlayer->ChangeState(NAME_Spectating);
		return newPlayer;
	}

	return newPlayer;
} 

Here is my custom SpawnPlayerController.

SpawnPlayerController:


APlayerController* AEliminationGameMode::SpawnPlayerController(FVector const& spawnLocation, FRotator const& spawnRotation, TSubclassOf<APlayerController> pcClass)
{
	APlayerController* pc;
	FActorSpawnParameters spawnInfo;
	ABaseGameState* const gameState = GetGameState<ABaseGameState>();
	spawnInfo.Instigator = Instigator;

	pc = GetWorld()->SpawnActor<APlayerController>(pcClass, spawnLocation, spawnRotation, spawnInfo);

	ABasePlayerController* castedPC = Cast<ABasePlayerController>(pc);
	if (castedPC->Team == Constants::MARINES)
	{
		gameState->AddMarinePlayer(castedPC);
	}
	else
	{
		gameState->BugPlayer = castedPC;
	}

	return pc;
}

Lastly, I override GetDefaultPawnClassForController so I can spawn the correct pawn for each controller.

GetDefaultPawnClassForController:


UClass* AEliminationGameMode::GetDefaultPawnClassForController(AController* inController)
{
	UClass* result;

	// See what type of controller was passed in.
	ABasePlayerController* baseController = Cast<ABasePlayerController>(inController);
	if (baseController == Constants::MARINES)
	{
		result = MarinePawnClass;
	}
	else
	{
		result = BaseSpectatorPawnClass;
	}

	return result;
}

I know I have some cleanup/optimization that can be done with this code. I’m sure it will change as I add more stuff to my game. I am always looking for ways to become a better programmer so if anyone has any ideas/suggestions on how I could do this better, please let me know. Hopefully, this can help you get started with what you want to do. Let me know if you have any other questions.

Joshua