FConstPlayerControllerIterator

김지윤·2025년 2월 16일

UE5_MultiplayerShooter

목록 보기
12/25

이번에 포스팅할 내용은 리스폰 기능이다.
죽은 플레이어는 곧 다시 레벨에 스폰된다. 레벨에 배치된 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를 선택해 스폰시키는 모습을 확인할 수 있다.
언리얼에서 제공하는 프레임워크 중 참 유용한 게 많다.

profile
공부한 거 시간 날 때 작성하는 곳

0개의 댓글