UMG을 처음 들어보았는데 이는 stand for Unreal Motion Graphic이다.
단어만 보면 너무 포괄적이라 무슨 말인지 모르겠는데, 의미적으로는 UI로 통하는 듯 하다.
참고로 UMG를 사용하기 위해서는 빌드세팅 C#파일에 'UMG' string을 추가해야한다.
새로운 일인칭 프로젝트를 생성해서 '유저인터페이스 → 위젯 블루프린트'를 만들고, class로 UserWidget을 상속한 클래스를 생성하자.
// MyHUD.h
UCLASS()
class FIRSTPERSONEXAMPLE_API UMyHUD : public UUserWidget
{
GENERATED_BODY()
public:
// widget 프로퍼티 선언
UPROPERTY(meta = (BindWidget))
class UTextBlock* AmmoText;
};
UserWidget 클래스에는 TextBlock 프로퍼티를 선언한다.
// FirstPersonExampleGamemode.h
UCLASS(minimalapi)
class AFirstPersonExampleGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AFirstPersonExampleGameMode();
public:
// widget의 클래스를 담음
UPROPERTY()
TSubclassOf<UUserWidget> HUD_Class;
// widget의 주소
UPROPERTY()
UUserWidget* CurrentWidget;
};
GameMode 클래스의 header에는 widget의 클래스, widget이 들어갈 주소를 담는 프로퍼티를 선언한다.
// FirstPersonExampleGameMode.cpp
AFirstPersonExampleGameMode::AFirstPersonExampleGameMode()
: Super()
{
/.../
// MyHUD 오브젝트 찾아오기
static ConstructorHelpers::FClassFinder<UMyHUD> UI_HUD(TEXT("WidgetBlueprint'/Game/WBP_HUD.WBP_HUD_C'"));
if (UI_HUD.Succeeded())
{
HUD_Class = UI_HUD.Class;
// HUD_Class에 해당하는 class의 widget을 만드는 함수
CurrentWidget = CreateWidget(GetWorld(), HUD_Class);
if (CurrentWidget)
{
// view port로 widget을 추가
CurrentWidget->AddToViewport();
// CurrentWidget->RemoveFromViewport();
}
}
}
GameMode.cpp에서는 MyHUD오브젝트를 가져와서 헤더에서 선언한 widget의 클래스를 생성하여 담고 view에 추가한다.
// FirstPersonExampleCharacter.h
UCLASS(config=Game)
class AFirstPersonExampleCharacter : public ACharacter
{
protexted:
// UI 새로고침 함수
void RefreshUI();
/.../
// Ammo 관련 변수 선언
int32 AmmoCount = 5;
int32 MaxAmmoCount = 5;
}
FPS 프로젝트를 생성하면 기본적으로 포함되어있던 Character 클래스의 헤더에 Ammo변수를 선언한다.
RefreshUI함수를 통해 UI 내용을 새로고침하는 함수를 선언한다.
// FirstPersonExampleCharacter.cpp
/.../
// 헤더 추가
#include "FirstPersonExampleGameMode.h"
#include "MyHUD.h"
#include "Components/TextBlock.h"
/.../
void AFirstPersonExampleCharacter::BeginPlay()
{
/.../
// UI 새로고침
RefreshUI();
}
/.../
void AFirstPersonExampleCharacter::RefreshUI()
{
AFirstPersonExampleGameMode* GameMode = Cast<AFirstPersonExampleGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
if (GameMode)
{
UMyHUD* MyHUD = Cast<UMyHUD>(GameMode->CurrentWidget);
if (MyHUD)
{
const FString AmmoStr = FString::Printf(TEXT("Ammo %01d/%01d"), AmmoCount, MaxAmmoCount);
MyHUD->AmmoText->SetText(FText::FromString(AmmoStr));
}
}
}
void AFirstPersonExampleCharacter::OnFire()
{
// Ammo가 없는 경우 쏘지않도록
if (AmmoCount <= 0)
return;
AmmoCount--;
// UI 새로고침
RefreshUI();
// GameMode 가져오기
AFirstPersonExampleGameMode* GameMode = Cast<AFirstPersonExampleGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
if (GameMode)
{
UMyHUD* MyHUD = Cast<UMyHUD>(GameMode->CurrentWidget);
if (MyHUD)
{
const FString AmmoStr = FString::Printf(TEXT("Ammo %01d/%01d"), AmmoCount, MaxAmmoCount);
MyHUD->AmmoText->SetText(FText::FromString(AmmoStr));
}
}
/.../
}
/.../
cpp에는 Ammo의 Count관련 처리 및 GameMode를 가져와서 GameMode 안의 HUD에 Ammo관련 Text를 띄우는 동작을 작성한다. Refresh로 게임시작시 미리 UI Text를 새로고침한다.
코드를 작성하고 컴파일 후 안되길래 뭘 빼먹었지.. 하고 있었는데, 알고보니 아직 블루프린트에 익숙하지 않은 모양인지 HUB 블루프린트 클래스에서 '클래스 세팅 → 부모 클래스'를 설정해서 블루프린트 클래스의 부모를 위에서 작성한 MyHUD코드로 바꾸는 것을 빼먹었다.