티스토리 뷰

🟩 오늘의 목표

  • UMG 위젯의 기초 디자인 개념과 HUD 시스템을 이해한다.
  • C++에서 UMG 위젯을 생성하고 화면에 표시하는 로직을 구현한다.
  • GameState의 데이터를 HUD 위젯과 실시간으로 연동하는 두 가지 방식(Binding, SetText)을 습득한다.

🟧 1. UMG 위젯 기초 디자인 이해하기

🟦 HUD와 위젯 블루프린트

  • HUD (Heads-Up Display): 플레이어에게 체력, 점수, 미니맵 등 게임 상황을 전달하는 인터페이스다.
  • UMG (Unreal Motion Graphics): Widget Blueprint를 사용하여 시각적으로 UI를 설계하는 강력한 시스템이다.
  • 위젯 블루프린트 구성: UI 요소를 배치하는 Designer 탭과 로직을 작성하는 Graph 탭으로 나뉜다.

🟦 UI 요소와 디자인 레이아웃

  • Canvas Panel: UI 요소들을 자유롭게 배치할 수 있는 도화지 역할을 하는 기본 레이아웃 패널이다.
  • 주요 UI 요소: Text Block(정보 표시), Button(클릭 이벤트), Progress Bar(게이지 표시) 등이 있다.
  • 폰트 설정: 외부 폰트(.ttf, .otf)를 임포트하여 Font Family를 지정함으로써 기본 Roboto 외의 스타일을 적용할 수 있다.

🟧 2. HUD Widget 생성 및 화면 표시

🟦 C++ 기반 위젯 생성 준비

  • 위젯은 보통 플레이어의 입력 및 상호작용과 직결되므로 PlayerController에서 관리한다.
  • Build.cs 설정: CreateWidget 기능을 사용하기 위해 PublicDependencyModuleNames에 UMG 모듈을 반드시 추가해야 한다.
// SpartaProject.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG" });

🟦 위젯 생성 및 출력 로직

TSubclassOf<UUserWidget>을 사용해 에디터에서 생성한 위젯 클래스를 참조하고, CreateWidget과 AddToViewport 함수로 화면에 띄운다.

// SpartaPlayerController.cpp
void ASpartaPlayerController::BeginPlay()
{
    Super::BeginPlay();
    // HUD 위젯 생성 및 표시
    if (HUDWidgetClass)
    {
        HUDWidgetInstance = CreateWidget<UUserWidget>(this, HUDWidgetClass);
        if (HUDWidgetInstance)
        {
            HUDWidgetInstance->AddToViewport();
        }
    }
}

🟧 3. HUD 위젯과 데이터 실시간 연동

🟦 연동 방식 비교 (Binding vs SetText)

  • Binding: 위젯 속성을 함수에 묶어 매 프레임 데이터를 읽어오는 방식이다. 구현이 직관적이지만 퍼포먼스에 부담을 줄 수 있다.
  • SetText: 데이터가 변경될 때나 특정 주기에 맞춰 직접 텍스트를 갱신하는 방식이다. 성능 면에서 더 유리하다.

🟦 SetText 방식을 이용한 HUD 갱신 구현

GameState에서 Timer를 사용해 주기적으로 UpdateHUD를 호출하고, GetWidgetFromName을 통해 위젯 내 요소를 찾아 텍스트를 업데이트한다.

// SpartaGameState.cpp의 UpdateHUD 로직
void ASpartaGameState::UpdateHUD()
{
    if (ASpartaPlayerController* SpartaPlayerController = Cast<ASpartaPlayerController>(GetWorld()->GetFirstPlayerController()))
    {
        if (UUserWidget* HUDWidget = SpartaPlayerController->GetHUDWidget())
        {
            // 이름으로 텍스트 블록을 찾아 데이터 갱신
            if (UTextBlock* TimeText = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("Time"))))
            {
                float RemainingTime = GetWorldTimerManager().GetTimerRemaining(LevelTimerHandle);
                TimeText->SetText(FText::FromString(FString::Printf(TEXT("Time: %.1f"), RemainingTime)));
            }
            
            if (UTextBlock* ScoreText = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("Score"))))
            {
                if (USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(GetGameInstance()))
                {
                    ScoreText->SetText(FText::FromString(FString::Printf(TEXT("Score: %d"), SpartaGameInstance->TotalScore)));
                }
            }
        }
    }
}

🟧 핵심 요약 및 주의사항

  • UMG 모듈 추가 : Build.cs에 UMG를 추가하지 않으면 위젯 관련 코드 빌드 시 링크 에러가 발생한다.
  • Hierarchy 관리 : Canvas Panel 아래에 요소들이 적절히 배치되었는지 계층 구조를 항상 확인해야 한다.
  • 위젯 이름 : GetWidgetFromName을 사용하려면 Designer 탭에서 해당 요소의 이름을 코드와 동일하게(Time, Score 등) 설정해야 한다.
  • 주기적 업데이트 : BeginPlay에서 HUDUpdateTimerHandle을 통해 적절한 주기(예: 0.1초)로 위젯을 갱신하면 실시간성을 확보할 수 있다.