Muzzil에서 카메라 구조와 미니맵 구현을 정리했다.


카메라 구조

가장 큰 변화는 PlayerCharacter의 카메라 처리 방식이다.
기존처럼 SpringArm에 카메라를 그냥 붙여서 따라오게 두는 게 아니라, 시작 시점의 카메라 뷰를 기준으로 고정 카메라 계산값을 따로 저장하고, 뒤로 이동할 때는 별도의 ACameraActor를 써서 시점을 멈추는 구조로 바뀌었다.

앞 방향으로 갈 때만 카메라가 따라가고, 뒤로 갈 때는 이 기준점을 멈추게 했다.

const bool bCanCameraFollowForward =
    PlayerDirection > 0 &&
    (!bUsingFrozenCameraActor || ActorLocation.X > FrozenCameraCenterLocation.X);

if (bCanCameraFollowForward)
{
    ScrollAnchorLocation.X = FMath::Max(ScrollAnchorLocation.X, ActorLocation.X + CameraForwardOffset);
    ScrollAnchorLocation.Y = ActorLocation.Y;
    ScrollAnchorLocation.Z = ActorLocation.Z;
}

image-1778847187425

뒤로 이동할 때는 현재 가운데위치를 저장하고, 그 위치를 기준으로 한 transform을 계산해서 CameraActor를 생성하거나 재사용한다.

if (PlayerDirection < 0)
{
    if (!bHasFrozenCameraCenter)
    {
        FrozenCameraCenterLocation = ScrollAnchorLocation;
        bHasFrozenCameraCenter = true;
    }

    const FTransform FrozenCameraTransform =
        GetFrozenCameraTransformForCenter(FrozenCameraCenterLocation);

    if (!FrozenCameraActor)
    {
        FrozenCameraActor = GetWorld()->SpawnActor(
            ACameraActor::StaticClass(),
            FrozenCameraTransform,
            SpawnParams);
    }

이후에는 MainCameraComponent의 프로젝션, OrthoWidth, ClipPlane, FOV, PostProcessSettings까지 FrozenCameraActorUCameraComponent로 복사한다.
그리고 실제 시점은 플레이어가 아니라 이 카메라 액터로 넘긴다.

PlayerController->SetViewTarget(FrozenCameraActor);
bUsingFrozenCameraActor = true;

image-1778847171575

반대로 다시 앞으로 움직이기 시작하면, 플레이어가 얼려 둔 중심점 FrozenCameraCenterLocation을 다시 넘어섰는지 보고 원래 플레이어 시점으로 복귀시킨다.

if (bUsingFrozenCameraActor)
{
    if (GetActorLocation().X <= FrozenCameraCenterLocation.X)
    {
        PlayerController->SetViewTarget(FrozenCameraActor);
        return;
    }

    PlayerController->SetViewTarget(this);
    bUsingFrozenCameraActor = false;
    bHasFrozenCameraCenter = false;
}

SpringArm을 완전히 삭제한 것은 아니지만, 실제 시점 제어의 기준을 SpringArm의 부착 관계에서 고정 anchor + 별도 camera actor 전환으로 구현했다.
필드도 그 방향에 맞게 FrozenCameraActor, FrozenCameraCenterLocation, CameraViewOffsetFromScrollAnchor, CameraViewRotation 중심으로 정리되어 있다.


미니맵 구조

UMinimapWidget이 스스로 기준 적 소환 범위 위에 있는 적과 플레이어를 찍는 방식으로 구현했다.

스테이지 기준점을 통해 미니맵에서의 플레이어와 적의 위치를 설정한다.

FVector2D PlayerPosition = WorldToMinimapPosition(PlayerPawn->GetActorLocation(), DrawSize);
...
FVector2D EnemyPosition = WorldToMinimapPosition(Enemy->GetActorLocation(), DrawSize);
const FTransform SpawnAreaTransform = TargetSpawner->GetSpawnAreaTransform();
const FVector Extent = TargetSpawner->GetSpawnAreaExtent();
const FVector LocalLocation = SpawnAreaTransform.InverseTransformPosition(WorldLocation);
const float RatioX = FMath::Clamp((LocalLocation.X + Extent.X) / (Extent.X * 2.0f), 0.0f, 1.0f);

스포너가 회전하거나 이동해도 같은 기준축으로 미니맵을 그릴 수 있다.

image-1778847351913