Hello dear community,
I am developing a little racing game with multiplayer capabilites in UE4.12. However, I noticed that, whenever I start it in the editor with more than one player, only one PlayerController is spawned, although 2 pawns are created. I can control them both, once I swap the viewport. How do I configure the game, so that for each Player that logged in, a PlayerController gets spawned? I already digged into the GameMode.cpp but it look all fine to me. Can somebody please explain this behavior and/or post a solution?
Here is the content of my ARacingGameMode.cpp:
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "RacingGame.h"
#include "RacingGameGameMode.h"
#include "RacingGamePawn.h"
#include "RacingGameGameState.h"
#include "RacingGameHud.h"
#include "GameFramework/GameNetworkManager.h"
#include "Matinee/MatineeActor.h"
void ARacingGameGameMode::Tick(float deltaSeconds)
{
Super::Tick(deltaSeconds);
if (m_CalcBuffer > 0) {
m_CalcBuffer -= deltaSeconds;
}
else{
CalculatePlayerPositions();
}
}
void ARacingGameGameMode::EndMatch()
{
Super::EndMatch();
UE_LOG(LogTemp, Warning, TEXT("Match has ended."));
}
ARacingGameGameMode::ARacingGameGameMode() {
PlayerControllerClass = ARacingGamePlayerController::StaticClass();
GameStateClass = ARacingGameGameState::StaticClass();
PlayerStateClass = ARacingGamePlayerState::StaticClass();
DefaultPawnClass = ARacingGamePawn::StaticClass();
static ConstructorHelpers::FClassFinder <AHUD> MyDefaultHUD(TEXT("/Game/RacingGameContent/HUD/BP_RacingGameHud"));
HUDClass = (UClass*)MyDefaultHUD.Class;
this->bStartPlayersAsSpectators = false;
bDelayedStart = false;
m_MapCheckPoints = TArray<AActor*>();
m_SpawnPoints = TArray<AActor*>();
}
void ARacingGameGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
m_PlayerControllers.Add(Cast<ARacingGamePlayerController>(NewPlayer));
Cast<ARacingGamePlayerState>(NewPlayer->PlayerState)->m_LastCheckpointPosition = m_MapCheckPoints[0]->GetActorLocation();
Cast<ARacingGamePlayerState>(NewPlayer->PlayerState)->m_LastCheckpointRotation = m_MapCheckPoints[0]->GetActorRotation();
}
void ARacingGameGameMode::CalculatePlayerPositions()
{
m_PlayerControllers.Sort([](const ARacingGamePlayerController& A, const ARacingGamePlayerController& B) {
ARacingGamePlayerState* _StateA = Cast<ARacingGamePlayerState>(A.PlayerState);
ARacingGamePlayerState* _StateB = Cast<ARacingGamePlayerState>(B.PlayerState);
ARacingGamePawn* _pawnA = Cast<ARacingGamePawn>(A.GetPawn());
ARacingGamePawn* _pawnB = Cast<ARacingGamePawn>(B.GetPawn());
if (A.CalcPositionScore() == B.CalcPositionScore())
{
FVector _distA = _StateA->m_LastCheckpointPosition - _pawnA->GetActorLocation();
FVector _distB = _StateB->m_LastCheckpointPosition - _pawnB->GetActorLocation();
if (_distA.Size() > _distB.Size())
{
return true;
}
else if (_distA.Size() < _distB.Size())
{
return false;
}
}
if (A.CalcPositionScore() > B.CalcPositionScore()) {
return true;
}
else {
return false;
}
});
uint8 _index = 0;
for (ARacingGamePlayerController* _controller : m_PlayerControllers) {
Cast<ARacingGamePlayerState>(_controller->PlayerState)->m_PlayerPosition = _index+1;
_index++;
}
}
void ARacingGameGameMode::PreInitializeComponents()
{
Super::PreInitializeComponents();
FActorSpawnParameters SpawnInfo;
SpawnInfo.Instigator = Instigator;
SpawnInfo.ObjectFlags |= RF_Transient; // We never want to save game states or network managers into a map
// Fallback to default GameState if none was specified.
if (GameStateClass == NULL)
{
//UE_LOG(LogGameMode, Warning, TEXT("No GameStateClass was specified in %s (%s)"), *GetName(), *GetClass()->GetName());
GameStateClass = ARacingGameGameState::StaticClass();
}
GameState = ()->SpawnActor<ARacingGameGameState>(GameStateClass, SpawnInfo);
()->GameState = GameState;
if (GameState)
{
GameState->AuthorityGameMode = this;
}
// Only need NetworkManager for servers in net games
()->NetworkManager = GetWorldSettings()->GameNetworkManagerClass ? ()->SpawnActor<AGameNetworkManager>(GetWorldSettings()->GameNetworkManagerClass, SpawnInfo) : NULL;
InitGameState();
}
void ARacingGameGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
UWorld* World = ();
// save Options for future use
OptionsString = Options;
UClass* const SessionClass = GetGameSessionClass();
FActorSpawnParameters SpawnInfo;
SpawnInfo.Instigator = Instigator;
SpawnInfo.ObjectFlags |= RF_Transient; // We never want to save game sessions into a map
GameSession = World->SpawnActor<AGameSession>(SessionClass, SpawnInfo);
GameSession->InitOptions(Options);
GameSession->MaxPlayers = 2;
if (GetNetMode() != NM_Standalone)
{
FOnlineSessionSettings* SessionSettings = NULL;
IOnlineSessionPtr SessionInt = Online::GetSessionInterface(World);
if (SessionInt.IsValid())
{
SessionSettings = SessionInt->GetSessionSettings(GameSession->SessionName);
}
// Attempt to login, returning true means an async login is in flight
if (!SessionSettings && !GameSession->ProcessAutoLogin())
{
GameSession->RegisterServer();
}
}
for (TActorIterator<APlayerStart> ActorItr(()); ActorItr; ++ActorItr)
{
// Same as with the Object Iterator, access the subclass instance with the * or -> operators.
m_SpawnPoints.Add(*ActorItr);
}
for (TActorIterator<ARacingGameCheckpoint> ActorItr(()); ActorItr; ++ActorItr)
{
// Same as with the Object Iterator, access the subclass instance with the * or -> operators.
m_MapCheckPoints.Add(*ActorItr);
}
SetMatchState(MatchState::EnteringMap);
}
AActor* ARacingGameGameMode::ChoosePlayerStart_Implementation(AController* Player)
{
UE_LOG(LogTemp, Warning, TEXT("Choose player start executed."))
// Choose a player start
APlayerStart* FoundPlayerStart = NULL;
ARacingGamePlayerController* _PlayerController = Cast<ARacingGamePlayerController>(Player);
UClass* PawnClass = GetDefaultPawnClassForController(_PlayerController);
AActor* SpawnPoint = m_SpawnPoints[0];
m_SpawnPoints.RemoveAt(0);
return SpawnPoint;
}
void ARacingGameGameMode::StartMatch()
{
if (HasMatchStarted())
{
// Already started
return;
}
//Let the game session override the StartMatch function, in case it wants to wait for arbitration
if (GameSession->HandleStartMatchRequest())
{
UE_LOG(LogTemp, Warning, TEXT("MatchStartHandleRequest has been executed."));
return;
}
UE_LOG(LogTemp, Warning, TEXT("Internal MatchState set to InProgress. (Starting Match)"));
Cast<ARacingGameGameState>(GameState)->m_MaxPlayers = GameSession->MaxPlayers;
Cast<ARacingGameGameState>(GameState)->m_MaxLaps = m_maxLaps;
Cast<ARacingGameGameState>(GameState)->m_NumCheckPoints = m_MapCheckPoints.Num();
SetMatchState(MatchState::InProgress);
}
bool ARacingGameGameMode::ReadyToStartMatch_Implementation()
{
UE_LOG(LogTemp, Warning, TEXT("Game session size %s"), *FString::FromInt(GameSession->MaxPlayers))
// If bDelayed Start is set, wait for a manual match start
if (bDelayedStart)
{
return false;
}
// By default start when we have > 0 players
if (GetMatchState() == MatchState::WaitingToStart)
{
if (NumPlayers + NumBots == GameSession->MaxPlayers)
{
return true;
}
}
return false;
}