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;
}

뒤로 이동할 때는 현재 가운데위치를 저장하고, 그 위치를 기준으로 한 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까지 FrozenCameraActor의 UCameraComponent로 복사한다.
그리고 실제 시점은 플레이어가 아니라 이 카메라 액터로 넘긴다.
PlayerController->SetViewTarget(FrozenCameraActor);
bUsingFrozenCameraActor = true;

반대로 다시 앞으로 움직이기 시작하면, 플레이어가 얼려 둔 중심점 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);
스포너가 회전하거나 이동해도 같은 기준축으로 미니맵을 그릴 수 있다.

'TIL' 카테고리의 다른 글
| 일주일 게임잼 제작 ~ 발표 (0) | 2026.05.21 |
|---|---|
| Delegate 쓰면 좋은 구조, 주의점 (0) | 2026.05.19 |
| "Git LFS 한도 초과." "?에셋이 몇 갠데" "없어" (0) | 2026.05.14 |
| 5/13 팀 작업 시작, 캐릭터 이동, 게임 시스템 구조 (0) | 2026.05.13 |
| 5/12 팀 작업 준비의 준비 프로젝트 (0) | 2026.05.12 |