UE HUD : UI 구현
- HUD(Heads-Up Display) : 플레이어에게 제공할 데이터를 화면 상에 띄우는 것
- 엔진마다 다르나 UE에서는 2가지 방법을 제공
Canvas 기반 HUD
AHUD
클래스를 상속받아 구현
- 기본적인 2D 그리기 작업(텍스트, 이미지 등) 가능
- 레거시(구버전) 방식. 간단한 HUD에 적합
UMG (Unreal Motion Graphic)
- Widget BP를 이용해서 제작
- 더 직관적이고 강력한 HUD 디자인 가능
- 새로운 방식. 최근에는 대체로 이 방식으로 구현.
- 다양한 위젯(Text, Button, Image 등)을 사용해서 HUD 제작
UI 생성
- UE에서 UI를 시각적으로 설계할 수 있도록 제공되는 에디터용 BP
- 개발자는 이 BP 안에서 TextBlock, Button, Image 등 다양한 UI 요소를 배치해서 작업 가능
- 이렇게 만든 Widget BP는 게임 화면에 표시되는 UI가 됨
- Contents Browser에서 우클릭 > User Interface > Widget Blueprint 선택

Designer
탭 : 필요한 UI를 배치.
Graph
탭 : BP 이벤트 그래프 로직을 작성
UI 요소
- 디자이너탭의 왼쪽에
Palette
탭에서 배치할 UI 요소들을 볼 수 있음.
- Text Block: 캐릭터 체력, Score(점수), Time(남은 시간) 등 텍스트를 보여줄 때
- Button: “Start Game”, “Quit Game” 등 클릭 이벤트가 필요한 메뉴 버튼
- Progress Bar: 체력 게이지나 로딩 게이지 등을 시각적으로 표시할 때
- 이 외에도 다양한 요소들이 있음.
UI 적용
- 현재 레벨, 남은 시간, 점수를 표시해 줄 것.
- 보통 UI 요소들은
PlayerController
에서 구현함.
- 이유는 Widget이 플레이어의 입력과 상호작용하며 화면에 표시되는 요소이기 때문
~~
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")
TSubclassOf<UUserWidget> HUDWidgetClass;
~~
- Widget 클래스의 원본을 BP에서 할당받아 사용할 것
위젯 코드 제어 설정
- 코드로 위젯을 제어하려면
UMG
모듈을 빌드 설정에 추가해줘야 함.
[프로젝트명].Build.cs
코드에 추가

위젯 생성
~~
#include "Blueprint/UserWidget.h"
~~
void ASpartaPlayerController::BeginPlay()
{
Super::BeginPlay();
~~
if (HUDWidgetClass)
{
UUserWidget* HUDWidget = CreateWidget<UUserWidget>(this, HUDWidgetClass);
if (HUDWidget)
HUDWidget->AddToViewport();
}
}
CreateWidget<UUserWidget>(this, HUDWidgetClass)
: 위젯 인스턴스 생성.
UUserWidget::AddToViewport()
: 뷰포트에 띄우기
- 유니티와 다르게 생성했다고 바로 뜨는게 아니라 뷰포트에 수동으로 추가해주어야 함.
UI 데이터 연동 방법
방법 1. 데이터 바인딩
바인딩
- 디자이너 탭에서 점수를 표시할
Text Block
을 선택.
- Content >
Text
항목의 오른쪽에 있는 Bind
버튼 클릭 -> Create Binding 선택
- 이렇게하면 그래프 탭에 자동으로 함수가 생성될 것.
- 함수의 이름은 보통
GetText
같이 자동으로 지정되나 원하는대로 수정할 수 있음.
- 왼쪽
Functions
목록에서 함수를 클릭 > F2를 눌러 이름 변경
GameState
에서 Score
읽기
- 새로 만들어진 바인딩 함수 안에서
GameState
를 가져와야함.
- BP에서 아래와 같이 작성

방법 2.
- PlayerController 에서 HUD를 켜주고
GameState
에 HUD를 보내서 데이터를 갱신
- 그 후에 직접
SetText
호출
PlayerController
의 Tick
함수나 타이머 이벤트에서 GameState
의 Score를 읽어와 HUD의 Text Block
에서 SetText
호출하는 것.
타이머 활용 갱신
Tick
에서 매 프레임 호출하는 것은 부담이므로 타이머를 활용해볼 것.
void ACp2GameState::BeginPlay()
{
Super::BeginPlay();
StartLevel();
GetWorldTimerManager().SetTimer(
HUDUpdateTimerHandle,
this,
&ACp2GameState::UpdateHUD,
0.1f,
true
);
}
void ACp2GameState::UpdateHUD()
{
if (APlayerController* playerController = GetWorld()->GetFirstPlayerController())
{
if(ACp2PlayerController* cp2Controller = Cast<ACp2PlayerController>(playerController))
{
if(UUserWidget* HUDWidget = cp2Controller->GetHUDWidget())
{
if(UTextBlock* textTime = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("TxtTime"))))
{
float remainTime = GetWorldTimerManager().GetTimerRemaining(LevelTimerHandle);
textTime->SetText(FText::FromString(FString::Printf(TEXT("Time : %.1f"), remainTime)));
}
if (UTextBlock* textScore = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("TxtScore"))))
{
if(UGameInstance* gameInst = GetGameInstance())
{
UCp2GameInstance* cp2GameInst = Cast<UCp2GameInstance>(gameInst);
if(cp2GameInst)
{
textScore->SetText(FText::FromString(FString::Printf(TEXT("Score : %d"), cp2GameInst->TotalScore)));
}
}
}
if (UTextBlock* textLevel = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("TxtLevel"))))
textLevel->SetText(FText::FromString(FString::Printf(TEXT("Level : %d"), CurrentLevelIndex + 1)));
}
}
}
}
- 직접 만든 Getter 함수에서 HUD 객체를 받고, HUD 객체에서 이름 기반으로 위젯을 받을 수 있음.
HUDWidget->GetWidgetFromName(TEXT("TxtTime"))
: 이름 기반 위젯 찾기
- 반환 결과를 찾길 원하는 UI요소로 캐스트해서 사용.
- 단, 이름 기반으로 찾는 거라서 휴먼 에러가 우려됨 관련된 다른 함수도 찾아볼 것.
- 내부 구현이 for문으로 위젯 전체를 순회하는 방식이다.
- 아예 WBP 생성 전에
UserWidget
을 상속하고 필요한 데이터를 할당할 수 있도록 설정하는 것도 방법이 될 수 있을 듯.
SetText(...)
textTime->SetText(FText::FromString(FString::Printf(TEXT("Time : %.1f"), remainTime)));
- 설정할 내용을
FText
형태의 매개변수로 요구.
- 위의 예시는 포맷 형태로 문자열로 구성하기위해
FString::Printf
를 쓰고 이를
FText::FromString()
함수로 구성한 것.
- UE에서 사용하는 문자열 관련해서 좀 더 정리할 필요가 있음.
장단점
SetText
를 필요할 때만 UI를 업데이트할 수 있어 퍼포먼스에 유리
- 그러나 초급자에게는 Binding 방식이 코드를 많이 작성하지 않아도 되어 더 직관적.
메인 메뉴 UI
- 메인메뉴 전용으로 레벨을 만드는 것이 좋음 - 스타트 레벨/씬
- 메인메뉴는 화면 전체를 가릴 것이므로 이때 플레이어 입력받으면 캐릭터에게 반영되지 않도록 해야함.
게임 입력 vs UI 입력
- UE에서는 게임 입력과 UI 입력을 내부적으로 구분하고 있다.
- UI 또는 게임 중 하나만 입력 받도록하거나, UI와 게임 둘다 입력 받도록 할 수 있음.
APlayerController::SetInputMode(const FInputModeDataBase& InData)
PlayerController
에 내장된 SetInputMode
함수에 FInputModeDataBase
구조체를 상속한 옵션들을 사용해서 변경 가능.
SetInputMode
계열 함수를 사용해서 PlayerController
가 어느 입력을 우선으로 처리할지 결정 가능.
FInputModeUIOnly
- UI 전용 입력 모드
- 플레이어의 마우스 입력과 키 입력이 UI로 먼저 전달됨.
- 캐릭터 이동, 시야 회전 등 게임 월드 입력은 잠시 비활성화되고 버튼 클릭에 집중.
SetWidgetToFocus(TSharedPtr<SWidget> InWidgetToFocus)
멤버 함수를 사용해서 특정한 위젯에 포커스를 맞출 수도 있음.
- 이때,
PlayerController
의 멤버 변수 bShowMouseCursor
에 true
를 넣어주어
마우스 커서를 보이도록 해주면 좋음.
FInputModeGameOnly
- 게임 전용 입력 모드
- 플레이어의 입력을 월드 입력에 우선적으로 전달.
FInputModeGameAndUI
- UI, 게임 혼합 입력 모드
- 플레이어의 입력을 UI가 받도록 설정하되, 만약 UI가 처리하지 못하는 입력이라면 플레이어의 컨트롤러에게 처리할 기회를 넘김.