EDIT: I’m testing this solution in UE4 4.12.5, and I’m pretty sure the previous solution I wrote a year ago doesn’t work now. So I implemented a new solution. Pull request has been requested at https://github.com/EpicGames/UnrealEngine/pull/2679
in AGameMode::Login
- if (bSpectator || MustSpectate(NewPlayerController))
+ if (bSpectator || MustSpectate(NewPlayerController) || bStartPlayersAsSpectators)
and in AGameMode::HandleSeamlessTravelPlayer
- if (PC->PlayerState->bOnlySpectator)
+ if (PC->PlayerState->bOnlySpectator || bStartPlayersAsSpectators)
I’ve tested this using the blueprint 3rd person template, made sure it originally didn’t work, then applied the fix and made sure the fix worked. If anyone finds any weird side effects from this, would love to know.
------ OLD POST from a year ago kept for reference purposes -----
So I spent quite some time diving into the engine’s source code to see how the related parts work. I have a patch/fix that solves the problem as far as I can see at the moment. But I’m sure it’s not the proper fix due to the complexity of the code base.
With the bStartPlayersAsSpectator flag being on, it does set the PlayerController to spectate state, however whenever GameMode starts to handle match being started, it goes through AGameMode::HandleMatchHasStarted
which performs this code
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
APlayerController* PlayerController = *Iterator;
if ((PlayerController->GetPawn() == NULL) && PlayerCanRestart(PlayerController))
{
RestartPlayer(PlayerController);
}
}
The issue lies in the fact that because it is in the SpectateState, GetPawn() will always == to NULL. Thus it restarts the player. AND In RestartPlayer()
, there is no checking of the SpectateState, this is probably because RestartPlayer()
takes in an AController
. And GetSpectatorPawn is a APlayerController thing. So it requires some casting to check if it’s an APlayerController
and further more work.
A simple fix is to change the code to the following
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
APlayerController* PlayerController = *Iterator;
if ((PlayerController->GetPawnOrSpectator() == NULL) && PlayerCanRestart(PlayerController))
{
RestartPlayer(PlayerController);
}
}
Because we know it’s only the APlayerController
that needs to be restarted, we can use the GetPawnOrSpectator function.
However it is a simple fix because I haven’t checked where else uses RestartPlayer()
, as that would still kick an APlayerController out of spectate state. There also isn’t a way to change a PlayerController to spectate mode via blueprints, so any solution seems to rely on C++. An extra point to mention is that AGameMode::StartNewPlayer
does have checks for bStartPlayersAsSpectators
. So a proper fix would probably involve modifying RestartPlayer()
. I will probably look into it to see if I can implement a more complete reasonable fix.