Runtime PlayerStart creation

When the game starts, I want to spawn the player with a specific location and rotation - this must be set in code. I have deleted my default PlayerStart and I’ve tried overriding the game mode’s ChoosePlayerStart_Implementation by spawning a new PlayerStart and returning it, but this results in the player being invisible, not controllable… just completely broken. I don’t particularly need a PlayerStart, I just want to spawn the player at a specific loc/rot, but it looks like using a PlayerStart is the only way.


AActor* AMyGameGameMode::ChoosePlayerStart_Implementation(AController* Player)
{
    FActorSpawnParameters SpawnInfo;
    return GetWorld()->SpawnActor<APlayerStart>(FVector(0, 0, 150), FRotator(0, 45, 0), SpawnInfo);
}

I also tried having a PlayerStart present and teleporting it to the correct position each time a player spawns;


UWorld* World = GetWorld();
for (TActorIterator<APlayerStart> It(World); It; ++It)
{
    APlayerStart* PlayerStart = *It;
    PlayerStart->SetActorLocationAndRotation(FVector(0, 0, 150), FRotator(0, 0, 0));
    return PlayerStart;
}

More problems as usual and expected, this time the thing won’t move to the location and rotation I specify.

And finally I tried spawning directly - yet again, something so simple refuses to work.


return SpawnDefaultPawnAtTransform(Player, FTransform(FRotator(0, 45, 0), FVector(0, 50, 100), FVector::OneVector));

Overriding ChoosePlayerStart seems to be the right way, not sure why that doesn’t work. I’ve checked the source code, there is a function in AGameModeBase that lets you spawn the player at a transform instead of a PlayerStart. Also you can experiment a bit with starting players as spectators (you can set this in the game mode), and then respawning them.



/** Tries to spawn the player's pawn at a specific location */
UFUNCTION(BlueprintCallable, Category=Game)
virtual void RestartPlayerAtTransform(AController* NewPlayer, const FTransform& SpawnTransform);


I don’t have an answer for your situation, but I am needing to address this too.

Currently:
I am using PlayerStarts. I spawn 3 player characters in the Editor for testing multiplayer. On one of my test levels, the 2nd or 3rd will sometimes not spawn due to the setting in the Engine not being set to AdjustIfPossibleButAlwaysSpawn. In that case in my GameModeBase, my overriden PostLogin I check for the existence of the pawn. If I don’t find one, I run my PlayerDied functionality.

This functionality calls GetDefaultPawnClassForController, then calls GetWorld()->SpawnActor<AAWPlayerCharacter>(…) passing in the location and rotation.

Issues:

  • I am calling Possess on the Controller that I now see is depreciated.
  • When I go to spawn at the player’s last location on startup, it will always be spawning twice - once at the playerstart then at the correct location.

Plans:
I am going to override RestartPlayer(AController* NewPlayer) to call my soon to be refactored PlayerDied logic.

Research:
Amusing: void AGameModeBase::FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation) calls the depreciated Possess as well. Looks like I will need to be sure to call this method in mine.

Note:
AGameModeBase::InitNewPlayer uses FindPlayerStart to set the location and rotation of the controller.

I’m not sure this will be of any help, but FWIW, I see two differences in how I did this in my overridden ChoosePlayerStart_Implementation and it seems to work fine.

In the FActorSpawnParameters, I set:


SpawnInfo.bDeferConstruction = false;

and included the APlayerStart::StaticClass() in the first parameter of the SpawnActor. Definition of bDeferConstruction is “Determines whether the construction script will be run”. It appears to default to true in the struct. If your actor uses a BP construction script, I could see this causing a timing issue in some cases.