정찰 땐 다 같이 맵을 둘러보다가 본게임이 시작되면 술래와 하이더가 각자 자리로 흩어져야 함

 

캐릭터는 이미 정찰 단계에서 스폰돼 다 같이 맵을 둘러보고 있음

본게임 시작하면서 이미 존재하는 캐릭터의 위치를 옮기는 것! 

 

처음 스폰 위치를 역할별로 나누는 문제였다면 ChoosePlayerStart를 오버라이드해 스폰 지점 선택 로직을 바꾸면 됨

하지만 스폰은 이미 끝난 상태기 때문에 페이즈가 전환되는 한순간에 위치만 이동(텔레포트) 시켜야 함

PlayerStart 태그

APlayerStart는 원래 스폰 지점으로 쓰는 액터라 위치, 회전 정보를 갖고 있고 PlayerStartTag라는 이름표 필드도 기본으로 제공한다 -> 여기에 Tagger / Hider를 지정하면 됨

 

레벨에 PlayerStart를 술래 자리, 하이더 자리에 배치하고 각각의 Player Start Tag에 역할 이름을 입력

하이더는 여럿이므로 하이더용 PlayerStart를 서로 떨어뜨려 여러 개 

배치할 땐 바닥에서 살짝 띄워, 텔레포트 후 캡슐이 바닥에 파묻히지 않게 함

 

텔레포트 로직은 StartInGame이 이미 무거워서 TeleportPlayersToStarts 헬퍼로 분리

 

하는 일은 두 단계

태그별 수집

레벨의 PlayerStart를 전부 훑어 태그에 따라 두 배열로 나눔

TArray<APlayerStart*> TaggerStarts;
TArray<APlayerStart*> HiderStarts;

// TActorIterator: 레벨에 배치된 특정 타입 액터를 전부 순회하는 엔진 제공 반복자
for (TActorIterator<APlayerStart> It(GetWorld()); It; ++It)
{
    APlayerStart* Start = *It;
    if (!Start) continue;

    if (Start->PlayerStartTag == TEXT("Tagger"))      TaggerStarts.Add(Start);
    else if (Start->PlayerStartTag == TEXT("Hider"))  HiderStarts.Add(Start);
}

 

배치

각 캐릭터를 역할에 맞는 배열에서 하나씩 꺼내 옮김

int32 TaggerIdx = 0;
int32 HiderIdx = 0;

for (APlayerState* PS : GameState->PlayerArray)
{
    const AFDPlayerState* FDPS = Cast<AFDPlayerState>(PS);
    if (!FDPS) continue;

    APawn* Pawn = FDPS->GetPawn();
    if (!Pawn) continue;

    APlayerStart* Target = nullptr;
    if (FDPS->RoleTag == EFDRole::Tagger && TaggerStarts.Num() > 0)
    {
        Target = TaggerStarts[TaggerIdx % TaggerStarts.Num()];
        ++TaggerIdx;
    }
    else if (FDPS->RoleTag == EFDRole::Hider && HiderStarts.Num() > 0)
    {
        Target = HiderStarts[HiderIdx % HiderStarts.Num()];
        ++HiderIdx;
    }
    if (!Target) continue;

    Pawn->SetActorLocation(Target->GetActorLocation(), false);
    Pawn->SetActorRotation(Target->GetActorRotation());
}

 

하이더는 여럿인데 하이더용 PlayerStart가 하나뿐이면 모두 같은 좌표로 텔레포트돼 서로 안에 끼임

그래서 하이더용 PlayerStart를 여러 개 두고 인덱스 % 자리수 연산으로 캐릭터를 번갈아 나눠 담음

자리가 캐릭터보다 적어도 % 덕에 순환하며 배치되고, 자리가 넉넉하면 겹침 없이 흩어진다

Sweep은 끄기

SetActorLocation의 두 번째 인자 Sweep은 "출발지에서 목적지까지 경로를 훑어, 뭔가에 막히면 거기서 멈춤"이다

걸어서 이동할 땐 벽을 뚫지 않게 해주는 유용한 옵션이지만 텔레포트는 경로 자체가 없는 순간이동이니까 Sweep을 켜면 출발지와 목적지 사이의 벽에 막혀 목적지에 도착하지 못함

텔레포트는 SetPhase보다 먼저

// 위치부터 잡고 나서 phase를 바꾼다
TeleportPlayersToStarts();
FDGameState->SetPhase(EMatchPhase::InGame);

 

순서가 반대면 게임 시작!을 본 직후에 몸이 뿅 순간이동하는 어색한 장면이 연출됨 

상태 전환(phase)과 그에 딸린 연출(팝업)이 있을 때 실제 세계의 변화(위치 이동)를 연출보다 앞에 두는 것

 

서버가 SetActorLocation으로 폰을 옮기면 그 변화가 복제로 모든 클라이언트에 전파

+ Recent posts