언리얼 미니맵 구현하기

달봉·2024년 6월 9일

언리얼엔진5

목록 보기
12/12

미니맵을 구현할 때 카메라를 사용하는 방법과 사용하지 않는 방법으로 나누어 생각해볼 수 있습니다.

카메라를 사용하는 미니맵

장점
-복잡한 환경과 지형의 정확한 구현이 가능합니다
단점
-추가적인 카메라 랜더링과 랜더타겟 사용은 그래픽 리소스를 소모하기 때문에 성능에 영향을 줄 수 있습니다

카메라를 사용하지 않는 미니맵

장점
-카메라를 사용하지 않기 때문에 성능최적화 및 유연한 커스터마이징이 가능합니다
단점
-각 객체의 위치를 미니맵 좌표에 맞게 추가적인 변환 과정이 필요하며 정확한 위치와 형태를 나타내기 어렵습니다

각 방식의 장 단점이 뚜렷하기 때문에 인게임 미니맵은 카메라를 사용하고 전체 맵을 볼땐 사용하지 않는 방식을 혼합해서 사용하는 등 환경에 따라 다른 방식을 사용할 수 있습니다.

이번 포스트에서는 카메라를 사용하는 방식의 미니맵을 구현합니다

미니맵

언리얼에서 제공하는 USceneCaptureComponent2D 컴포넌트를 사용하는 방법입니다
카메라의 위치를 고정하기 위한 USpringArmComponent
화면을 캡처하여 RenderTarget에 기록하기 위한 카메라 USceneCaptureComponent2D
미니맵에 자신의 위치를 따로 표시하기 위해 랜더될 스프라이트 UPaperSpriteComponent
컴포넌트를 추가해줍니다

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Minimap, meta = (AllowPrivateAccess = "true"))
		class USpringArmComponent* MinimapCameraBoom;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Minimap, meta = (AllowPrivateAccess = "true"))
		class USceneCaptureComponent2D* MinimapCapture;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Minimap, meta = (AllowPrivateAccess = "true"))
		class UPaperSpriteComponent* MinimapSprite;

카메라암 컴포넌트 초기화

	#pragma region MinimapCameraSpringArm
	MinimapCameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	MinimapCameraBoom->SetupAttachment(RootComponent);
    //위에서 아래로 내려다 보는 각도를 위해 회전
	MinimapCameraBoom->SetWorldRotation(FRotator::MakeFromEuler(FVector(0.f, -90.f, 0.f)));
    //미니맵 자체가 회전하지 않게 고정(회전하는 미니맵을 원할 경우 true)
	MinimapCameraBoom->bUsePawnControlRotation = false;
	MinimapCameraBoom->bInheritPitch = false;
	MinimapCameraBoom->bInheritRoll = false;
	MinimapCameraBoom->bInheritYaw = false;
	#pragma endregion

카메라(캡처) 컴포넌트 초기화
이미지를 기록할 랜더타겟을 미리 생성해두고 진행합니다

	#pragma region MinimapCamera(SeceneCaptureComponent)
	MinimapCapture = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("MinimapCapture"));
    //카메라 암에 어태치
	MinimapCapture->SetupAttachment(MinimapCameraBoom);
    //카메라 투영 타입을 직교로 전환하여 거리감이 없는 이미지로 구현
	MinimapCapture->ProjectionType = ECameraProjectionMode::Orthographic;
    //카메라에서 캡처될 크기 ( 클수록 축소되 보이는 미니맵 )
	MinimapCapture->OrthoWidth = 3072;
    //캡처된 이미지를 기록할 랜더타겟 로드
	ConstructorHelpers::FObjectFinder<UCanvasRenderTarget2D> FOBJ_RenderTarget2D(TEXT("랜더타겟"));
	if (FOBJ_RenderTarget2D.Succeeded())
	{
		MinimapCapture->TextureTarget = FOBJ_RenderTarget2D.Object;
	}
	#pragma endregion

미니맵에서 표시될 스프라이트 초기화
스프라이트는 텍스처에서 CreateSprite 하여 생성

	#pragma region MinimapSprite
	MinimapSprite = CreateDefaultSubobject<UPaperSpriteComponent>(TEXT("MinimapSprite"));
	MinimapSprite->SetupAttachment(RootComponent);
    //Actor의 Foward방향과 일치화
	MinimapSprite->SetWorldRotation(FRotator::MakeFromEuler(FVector(90.f, 0.f, -90.f)));
	MinimapSprite->SetWorldScale3D(FVector(0.5f));
	MinimapSprite->SetWorldLocation(FVector(0.f,0.f,300.f));
    //인게임에서 보이지 않게 하는 옵션(캡처에서만 보이게 하는)
	MinimapSprite->bVisibleInSceneCaptureOnly = true;

	ConstructorHelpers::FObjectFinder<UPaperSprite> FOBJ_PaperSprite(TEXT("스프라이트"));
	if (FOBJ_PaperSprite.Succeeded())
	{
		MinimapSprite->SetSprite(FOBJ_PaperSprite.Object);
	}
	#pragma endregion



생성해둔 랜더타겟에 위에서 본 직교투영 화면과 Sprite이미지가 랜더된 모습

해당 랜더타겟을 CreateMaterial로 마테리얼화 시켜 UI에서 사용하면 미니맵이 됩니다.


+덤 Depth 기반 미니맵 / 특정 타입만 랜더

SceneCaptureComponent에는 화면을 캡처할때 어떤 방식으로 캡처할것인지를 정할 수 있고
특정 채널(레이어)이나 특정 객체만 캡처할 수도 있습니다.

미니맵을 위에서 보는 일반 이미지 대신 Depth갚을 통해 높낮이 구분이 되는 미니맵으로 변경해보겠습니다

CaptureSource에서 랜더타겟에 어떤값으로 기록될지 변경할 수 있습니다
깊이값을 Alpha채널에 기록하도록 RGB + Depth로 변경합니다.

MinimapCapture->CaptureSource = ESceneCaptureSource::SCS_SceneColorSceneDepth;

ShowFlags에서 원하지 않는 플래그를 제외시켜 랜더되지 않게 변경합니다.

MinimapCapture->ShowFlags.SetSkeletalMeshes(false);
...ETC

조명 및 기타 필요없는 플래그를 전부 제외시켜 미니맵 스프라이트만 보이지만
Alpha채널에 카메라 기준의 Depth값이 기록되어 있습니다

이렇게 기록한 Depth는 정규화 되어있지 않은 값으로 원하는 표현에 맞게 나누어서 사용합니다

인게임 표현
Depth값을 이용해 플레이어의 위치를 중간톤으로 놓고
플레이어보다 낮은 지형은 어둡게 플레이어보다 높은 지형은 밝게 표현하여 높낮이 표현이 되는 미니맵을 만들었습니다.

profile
개발 블로그

0개의 댓글