이번에 포스팅할 내용은 리스폰 기능이다.
죽은 플레이어는 곧 다시 레벨에 스폰된다. 레벨에 배치된 PlayerStart들 중 하나를 골라서 스폰하게 되는데, 이 중 다른 플레이어들과 가장 거리가 먼 PlayerStart를 찾는 기능을 구현했다.
그 과정에서 사용한 것이 FConstPlayerControllerIterator다.
언리얼에서 제공하는 반복자 프레임워크다.
// Iterators
typedef TIndexedContainerIterator<TArray, ElementType, SizeType> TIterator;
typedef TIndexedContainerIterator<const TArray, const ElementType, SizeType> TConstIterator;
반복자 템플릿을 통해 만들어진 두 개의 형식이다.
원하는 ElementType으로 TArray를 만들고, 반복자가 필요할 때 불러서 쓸 수 있다.
typedef TArray<TWeakObjectPtr<AController> >::TConstIterator FConstControllerIterator;
typedef TArray<TWeakObjectPtr<APlayerController> >::TConstIterator FConstPlayerControllerIterator;
typedef TArray<TWeakObjectPtr<ACameraActor> >::TConstIterator FConstCameraActorIterator;
typedef TArray<ULevel*>::TConstIterator FConstLevelIterator;
typedef TArray<TWeakObjectPtr<APhysicsVolume> >::TConstIterator FConstPhysicsVolumeIterator;
그 형식으로 만들어진 또 다른 형식 중 몇 개다.
사용법은 간단하다.
for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
APlayerController* PlayerController = It->Get();
// Do Something
}
평범하게 반복문 돌려서 쓰면 된다.
TWeakObjectPtr이기 때문에 Get()을 통해 값을 가져와 사용하면 된다.
원한다면 자신이 만든 PlayerController로 캐스트해서 사용하면 되겠다.
void ABlasterCharacter::ElimTimerFinished()
{
// ElimDelay만큼의 시간 이후 리스폰
if (ABlasterGameMode* BlasterGameMode = GetWorld()->GetAuthGameMode<ABlasterGameMode>())
{
BlasterGameMode->RequestRespawn(this, Controller);
}
}
캐릭터는 사망 이후 일정 시간이 지나면 GameMode에 리스폰을 요청한다.
void ABlasterGameMode::RequestRespawn(ACharacter* ElimmedCharacter, AController* ElimmedController)
{
if (ElimmedCharacter)
{
// 캐릭터 리셋, 디스트로이
ElimmedCharacter->Reset();
ElimmedCharacter->Destroy();
}
if (ElimmedController)
{
// 레벨에 배치된 PlayerStart 가져오기
TArray<AActor*> PlayerStarts;
UGameplayStatics::GetAllActorsOfClass(this, APlayerStart::StaticClass(), PlayerStarts);
// 자신을 제외한 플레이어들의 현재 위치 가져오기
TArray<FVector> PlayerLocations;
for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
APlayerController* PlayerController = It->Get();
if (PlayerController && PlayerController != ElimmedController && PlayerController->GetPawn())
{
PlayerLocations.Add(PlayerController->GetPawn()->GetActorLocation());
}
}
AActor* PlayerStartToSpawn = nullptr;
float MaxMinDistance = -1.0f;
for (AActor* PlayerStart : PlayerStarts)
{
FVector PlayerStartLocation = PlayerStart->GetActorLocation();
// 현재 PlayerStart와 모든 플레이어 간의 최소 거리 계산
float MinDistance = FLT_MAX;
for (const FVector& PlayerLocation : PlayerLocations)
{
float Distance = FVector::Distance(PlayerStartLocation, PlayerLocation);
if (Distance < MinDistance)
{
MinDistance = Distance;
}
}
// 최소 거리가 가장 큰 PlayerStart를 선택
if (MinDistance > MaxMinDistance)
{
MaxMinDistance = MinDistance;
PlayerStartToSpawn = PlayerStart;
}
}
// 선택된 PlayerStart에서 캐릭터 스폰
RestartPlayerAtPlayerStart(ElimmedController, PlayerStartToSpawn);
}
}
간단하게 작성해본 리스폰 로직이다.
이중 반복문을 통해 플레이어 간의 거리를 계산, 그 중 최소 거리가 가장 큰 PlayerStart에서 스폰시킨다.

더 멀리 있는 PlayerStart를 선택해 스폰시키는 모습을 확인할 수 있다.
언리얼에서 제공하는 프레임워크 중 참 유용한 게 많다.