Pawn spawning and possession after seamless travel.

Hey, I am making my first online multiplayer game. I want to make an among us like system where there are normal players and some random impostors. What I am trying to arrange these roles randomly and spawn and possess the respective pawns after on PostSeamlessTravel, but only sometimes characters actually get spawned and possessed. I think it might have to do with the client not being ready at this time. What should i do?

ABolinaGameMode::ABolinaGameMode()
{
	DefaultPawnClass = nullptr;

	NumImpostors = 1;
}

void ABolinaGameMode::StartMatch()
{
	Super::StartMatch();
	
}

/*
 * Creates an array of random character classes to be possessed
 * @return 
 */
TArray<TSubclassOf<ABolinaBaseCharacter>> ABolinaGameMode::InitializePendingRoles()
{

	TArray<TSubclassOf<ABolinaBaseCharacter>> pendingRoles;
	
	for (int i = 0; i < NumImpostors; i++)
	{
		pendingRoles.Add(IntruderClass);
		
	}

	for (int i = 0; i < GameState->PlayerArray.Num() - NumImpostors; i++)
	{
		pendingRoles.Add(CrewMateClass);
		
	}
	
	Test::Shuffle(pendingRoles);

	return pendingRoles;
}





void ABolinaGameMode::BeginPlay()
{
	Super::BeginPlay();
}



void ABolinaGameMode::AssignPlayerRolesAndSpawnCharacters() 
{
	TArray<TSubclassOf<ABolinaBaseCharacter>> pendingRoles = InitializePendingRoles();
	UWorld* world = GetWorld();
	
	for (APlayerState* playerState : GameState->PlayerArray)
	{
		APlayerController* pc = playerState->GetPlayerController();
		TSubclassOf<ABolinaBaseCharacter> roleClass = pendingRoles.Pop();
		AActor* playerStart = FindPlayerStart(pc);

		if (playerStart == nullptr)
		{
			GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Player Start is null");
		}

		
		ABolinaBaseCharacter* spawnedCharacter = world->SpawnActor<ABolinaBaseCharacter>(roleClass, playerStart->GetActorTransform());

		if (spawnedCharacter == nullptr)
		{
			GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Spawned is null");
		}
		
		pc->Possess(spawnedCharacter);
	}

}

void ABolinaGameMode::PostSeamlessTravel()
{
	Super::PostSeamlessTravel();

	AssignPlayerRolesAndSpawnCharacters();
	

}

void ABolinaGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
	Super::HandleSeamlessTravelPlayer(C);
		
}

UPDATE:

Now i did it like this:

// Copyright Epic Games, Inc. All Rights Reserved.

#include "BolinaGameMode.h"
#include "BolinaCharacter.h"
#include "BolinaIntruder.h"
#include "UObject/ConstructorHelpers.h"

#include "Camera/CameraActor.h"
#include "GameFramework/PlayerController.h"
#include "BolinaPlayerController.h"
#include "BolinaPlayerState.h"
#include "GameFramework/GameStateBase.h"
#include "GameFramework/PlayerStart.h"
#include "Kismet/GameplayStatics.h"
#include "Runtime/Core/Tests/Containers/TestUtils.h"


ABolinaGameMode::ABolinaGameMode()
{
	DefaultPawnClass = nullptr;

	NumImpostors = 1;
}

void ABolinaGameMode::StartMatch()
{
	Super::StartMatch();
	
}

/*
 * Creates an array of random character classes to be possessed
 * @return 
 */
TArray<TSubclassOf<ABolinaBaseCharacter>> ABolinaGameMode::InitializePendingRoles()
{

	TArray<TSubclassOf<ABolinaBaseCharacter>> pendingRoles;
	
	for (int i = 0; i < NumImpostors; i++)
	{
		pendingRoles.Add(IntruderClass);
		
	}

	for (int i = 0; i < GameState->PlayerArray.Num() - NumImpostors; i++)
	{
		pendingRoles.Add(CrewMateClass);
		
	}
	
	Test::Shuffle(pendingRoles);

	return pendingRoles;
}





void ABolinaGameMode::BeginPlay()
{
	Super::BeginPlay();
}



void ABolinaGameMode::AssignPlayerRolesAndSpawnCharacters() 
{
	TArray<TSubclassOf<ABolinaBaseCharacter>> pendingRoles = InitializePendingRoles();
	
	UWorld* world = GetWorld();

	FString message = FString::Printf(TEXT("Number of roles created = %d"), pendingRoles.Num());
	GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Red, message);
	
	for (APlayerState* playerState : GameState->PlayerArray)
	{
		APlayerController* pc = playerState->GetPlayerController();
		TSubclassOf<ABolinaBaseCharacter> roleClass = pendingRoles.Pop();
		AActor* playerStart = FindPlayerStart(pc);
		GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Player Handle");

		if (playerStart == nullptr)
		{
			GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Player Start is null");
		}

		
		ABolinaBaseCharacter* spawnedCharacter = world->SpawnActor<ABolinaBaseCharacter>(roleClass, playerStart->GetActorTransform());

		if (spawnedCharacter == nullptr)
		{
			GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Spawned is null");
		}
		
		pc->Possess(spawnedCharacter);
	}

}

void ABolinaGameMode::PostSeamlessTravel()
{
	Super::PostSeamlessTravel();

	

}

void ABolinaGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
	Super::HandleSeamlessTravelPlayer(C);
		
}



void ABolinaGameMode::ReceiverReady(APlayerController* PC)
{
	GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Spawn Me");

	nReceiverReady++;
	if (nReceiverReady == GameState->PlayerArray.Num())
	{
		AssignPlayerRolesAndSpawnCharacters();
		
	}
	
}


With this rpc:

void ABolinaPlayerController::BeginPlay()
{
	Super::BeginPlay();

	if (IsLocalController())
	{
		GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Blue, TEXT("Was called here"));
		ServerNotifyClientReady(this);
		
	}
	UE_LOG(LogTemp, Warning, TEXT("BolinaPlayerController start"));
}

void ABolinaPlayerController::ServerNotifyClientReady_Implementation(AController* P)
{
	
	
	ABolinaGameMode* GameMode = Cast<ABolinaGameMode>(GetWorld()->GetAuthGameMode());
	if (GameMode)
	{
		GameMode->ReceiverReady(this);
	}
	

}

This worked!

1 Like