Download

[Solved] How to keep spawned actor after GC

Hi there. I’m new to Unreal Engine and just started to create a multiplayer game. What I want is, the PlayerController can control two different pawns, one for RTS style camera movement, and actual character pawn for the other one. Currently, the camera pawn takes the default pawn slot. So I added a new pointer for character pawn. Based on my understanding of multiplayer game, the character pawn should be spawned on server side and replicated back to client (correct me if it’s wrong). I use a RPC function to execute spawn action on server side and it works as expect. But the spawned pawn will be quickly deleted. I’m not sure but it seems caused by GC.

Here is my code about the spawn logic:

PlayerController class header:



UCLASS()
class AStandardModePlayerController : public APlayerController
{
    GENERATED_UCLASS_BODY()

    virtual void PlayerTick(float DeltaTime) override;

    UPROPERTY(Category=PlayerController, EditAnywhere, BlueprintReadOnly, Replicated)
    TSubclassOf<AStandardModePlayerCharacter> CharacterPawnClass;
protected:
    virtual void BeginPlay() override;

    UFUNCTION(Server, Reliable)
    void SpawnCharacterPawn();
private:
    UPROPERTY(Category=PlayerController, VisibleAnywhere, Replicated)
    AStandardModePlayerCharacter* CharacterPawn;
};


PlayerController class CPP:



AStandardModePlayerController::AStandardModePlayerController(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    bReplicates = true;
    bShowMouseCursor = true;
    CharacterPawn = nullptr;
    PrimaryActorTick.bCanEverTick = true;
}


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

    SpawnCharacterPawn();
}

void AStandardModePlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> & OutLifetimeProps ) const
{
    DOREPLIFETIME(AStandardModePlayerController, CharacterPawnClass);
    DOREPLIFETIME(AStandardModePlayerController, CharacterPawn);
}


void AStandardModePlayerController::SpawnCharacterPawn_Implementation()
{
    // Only do spawn on server side
    if (GetLocalRole() != ROLE_Authority)
    {
        return;
    }

    // Spawn character pawn now
    if (CharacterPawn == nullptr)
    {
        if (CharacterPawnClass != nullptr)
        {
            CharacterPawn = GetWorld()->SpawnActor<AStandardModePlayerCharacter>(CharacterPawnClass->GetDefaultObject()->GetClass(), RootComponent->GetComponentLocation(), RootComponent->GetComponentRotation());
            CharacterPawn->SetOwner(this);
            CharacterPawn->SetRole(ENetRole::ROLE_Authority);
         }
         else
         {
             UE_LOG(LogInit, Error, TEXT("No pawn has been specified as character."));
         }
    }
}


Does anyone also encounter with this issue or implemented the same logic? Any response is appreciate. Thanks.

GC isn’t destroying it - but player pawns are spawned by the GameMode already. UE4’s Gameplay Framework typically already handles most basic stuff like this for you, it’s just a case of wrking out where to inject your own code.

Look at overriding “UClass* GetDefaultPawnClassForController_Implementation(AController* InController);” in your GameMode class. In my case, I’ve overridden it here to spawn a class specified by the player state:



UClass* AHT_GameModeBase::GetDefaultPawnClassForController_Implementation(AController* InController)
{
const AHT_PlayerState* ControllerPS = InController->GetPlayerState<AHT_PlayerState>();
if (ControllerPS && !ControllerPS->GetDefaultPawn().IsNull())
{
return ControllerPS->GetDefaultPawn().LoadSynchronous();
}

return Super::GetDefaultPawnClassForController_Implementation(InController);
}


Thanks for your answer, Jamsh. I’ve figured out what’s wrong on my side. After several times of debug, I found the spawn location is (0, 0, 0) which is dangerous while playing with TopDownSample default map. The spawned pawn will fell into dead zone directly at the (0, 0, 0) coordinate. As a consequence, I lose all the pawn just spawned.

My solution is using APlayerStart instance’s location instead of RootComponent’s like this:



void AStandardModePlayerController::SpawnCharacterPawn_Implementation()
{
    // Only do spawn on server side
    if (GetLocalRole() != ROLE_Authority)
    {
        return;
    }

    // Spawn character pawn now
    if (CharacterPawn == nullptr)
    {
        if (CharacterPawnClass != nullptr)
        {
            TArray<AActor*> StartPoints;
            UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerStart::StaticClass(), StartPoints);

            const FVector StartPoint = StartPoints.Num() > 0 ? StartPoints[FMath::RandRange(0, StartPoints.Num() - 1)]->GetActorLocation() : FVector::ZeroVector;

            CharacterPawn = GetWorld()->SpawnActor<AStandardModePlayerCharacter>( CharacterPawnClass->GetDefaultObject()->GetClass(), StartPoint, RootComponent->GetComponentRotation());
            CharacterPawn->SetOwner(this);
        }
        else
        {
            UE_LOG(LogInit, Error, TEXT("No pawn has been specified as character."));
        }
    }
}


Anyway, thanks for your time.