순위 기준은 오래 살아남은 순서

각 하이더의 생존 시간을 매 프레임 누적하는 것보다 좋은 방법이 있을 것 같아서 다시 찾음

 

본게임 시작 시각은 모두 같으니 얼마나 오래 살았나는 언제 잡혔나와 같은 정보

늦게 잡힐수록 오래 산 것이고, 끝까지 안 잡힌 하이더가 가장 오래 산 사람

따라서 각 하이더가 잡힌 시각 하나만 기록하면 된다

// FDPlayerState.h
UPROPERTY(Replicated)
float DeathServerTime = -1.f;   // -1이면 아직 안 잡힘(=생존 중)

 

하이더가 잡히는 순간 이미 있던 ResolveCapture에서 이 값을 찍음

FDHiderPS->bIsAlive = false;
FDHiderPS->DeathServerTime = FDGameState->GetServerWorldTimeSeconds();

 

정렬: 생존 여부로 먼저 가르고 그다음 사망 시각

게임이 끝나면(EndMatch) 서버가 하이더를 모아 정렬

Hiders.Sort([](const AFDPlayerState& A, const AFDPlayerState& B)
{
    const bool bAAlive = (A.DeathServerTime < 0.f); // 안 잡힘 = 끝까지 생존
    const bool bBAlive = (B.DeathServerTime < 0.f);

    if (bAAlive != bBAlive) return bAAlive;          // 생존자가 항상 앞
    if (bAAlive && bBAlive) return false;            // 둘 다 생존이면 순서 무관
    return A.DeathServerTime > B.DeathServerTime;    // 둘 다 잡혔으면 늦게 잡힌 쪽이 앞
});

 

둘 다 생존이면 우열 없음, 둘 다 잡혔으면 늦게 잡힌(사망 시각이 큰) 쪽이 앞

 

복제: 왜 TArray인가

정렬 결과를 화면에 뿌리려면 클라이언트로 복제해야 하는데

순위표는 이름, 등수, 생존시간을 담은 struct 목록

USTRUCT(BlueprintType)
struct FHiderRankEntry
{
    GENERATED_BODY()
    UPROPERTY(BlueprintReadOnly) FString PlayerName;
    UPROPERTY(BlueprintReadOnly) int32 Rank = 0;
    UPROPERTY(BlueprintReadOnly) float SurvivalTime = 0.f;
};

 

이걸 GameState에 TArray<FHiderRankEntry>로 복제 

순위는 순서가 있는 목록이므로 배열이 맞고 무엇보다 TArray는 복제가 되기 때문

 

아이템 인벤토리를 만들 때 TMap이 DOREPLIFETIME으로 복제되지 않아 슬롯을 개별 변수로 풀어야 했던 그 제약과 대비되는 지점

 

배열을 채운 뒤 서버는 직접 OnRep을 부름

FDGameState->HiderRankings.Empty();
for (int32 i = 0; i < Hiders.Num(); ++i)
{
    FHiderRankEntry Entry;
    Entry.PlayerName = Hiders[i]->GetPlayerName();
    Entry.Rank = i + 1;
    Entry.SurvivalTime = (Hiders[i]->DeathServerTime < 0.f)
        ? GameTimeLimit
        : (Hiders[i]->DeathServerTime - GameStartTime);
    FDGameState->HiderRankings.Add(Entry);
}
FDGameState->OnRep_HiderRankings();   // 리슨 호스트는 자동 호출 안 되므로 수동

 

 

생존 시간 계산은 "잡힌 시각 − 시작 시각"으로 생존 시간을 구함

+ Recent posts