icon안동민 개발노트

HUD 클래스 활용


 HUD(Heads-Up Display)는 게임 내에서 플레이어에게 중요한 정보를 표시하는 데 사용되는 UI 요소입니다.

 언리얼 엔진에서는 AHUD 클래스를 통해 HUD를 구현할 수 있습니다.

AHUD 클래스의 주요 기능과 특징

 AHUD 클래스는 다음과 같은 주요 기능을 제공합니다.

  1. 2D 그래픽 요소 그리기
  2. 게임 정보 표시
  3. UMG 위젯과의 통합
  4. 입력 처리
UCLASS()
class MYPROJECT_API AMyHUD : public AHUD
{
    GENERATED_BODY()
 
public:
    AMyHUD();
 
    virtual void DrawHUD() override;
    virtual void BeginPlay() override;
 
private:
    UPROPERTY()
    class UUserWidget* MainWidget;
};

HUD에 2D 그래픽 요소 그리기

 DrawHUD 함수를 오버라이드하여 2D 그래픽 요소를 그릴 수 있습니다.

void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    // 십자선 그리기
    float CrosshairSize = 10.0f;
    float ScreenCenterX = Canvas->SizeX / 2;
    float ScreenCenterY = Canvas->SizeY / 2;
 
    DrawLine(ScreenCenterX - CrosshairSize, ScreenCenterY, ScreenCenterX + CrosshairSize, ScreenCenterY, FLinearColor::Red);
    DrawLine(ScreenCenterX, ScreenCenterY - CrosshairSize, ScreenCenterX, ScreenCenterY + CrosshairSize, FLinearColor::Red);
 
    // 텍스트 그리기
    DrawText(FString("Hello, HUD!"), FLinearColor::White, 50, 50, nullptr, 1.5f);
}

HUD를 통한 게임 정보 표시

 플레이어의 체력, 점수 등의 게임 정보를 HUD에 표시할 수 있습니다.

void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    APlayerController* PC = GetOwningPlayerController();
    if (PC)
    {
        AMyPlayerState* PS = PC->GetPlayerState<AMyPlayerState>();
        if (PS)
        {
            FString HealthText = FString::Printf(TEXT("Health: %d"), FMath::RoundToInt(PS->GetHealth()));
            DrawText(HealthText, FLinearColor::Green, 50, 100, nullptr, 1.5f);
 
            FString ScoreText = FString::Printf(TEXT("Score: %d"), PS->GetScore());
            DrawText(ScoreText, FLinearColor::Yellow, 50, 150, nullptr, 1.5f);
        }
    }
}

HUD와 UMG 위젯의 통합

 HUD 클래스에서 UMG 위젯을 생성하고 관리할 수 있습니다.

void AMyHUD::BeginPlay()
{
    Super::BeginPlay();
 
    if (MainWidgetClass)
    {
        MainWidget = CreateWidget<UUserWidget>(GetOwningPlayerController(), MainWidgetClass);
        if (MainWidget)
        {
            MainWidget->AddToViewport();
        }
    }
}

동적 HUD 요소 생성 및 관리

 게임 상황에 따라 동적으로 HUD 요소를 생성하고 관리할 수 있습니다.

void AMyHUD::ShowDamageIndicator(float DamageAmount, FVector DamageLocation)
{
    FVector2D ScreenLocation;
    if (ProjectWorldLocationToScreen(DamageLocation, ScreenLocation))
    {
        UDamageIndicatorWidget* DamageWidget = CreateWidget<UDamageIndicatorWidget>(GetOwningPlayerController(), DamageIndicatorClass);
        if (DamageWidget)
        {
            DamageWidget->SetDamageAmount(DamageAmount);
            DamageWidget->SetPositionInViewport(ScreenLocation);
            DamageWidget->AddToViewport();
        }
    }
}

플레이어 상태에 따른 HUD 업데이트

 플레이어의 상태 변화에 따라 HUD를 업데이트할 수 있습니다.

void AMyHUD::UpdatePlayerStatus(float Health, int32 Ammo)
{
    if (MainWidget)
    {
        UMyMainWidget* MyMainWidget = Cast<UMyMainWidget>(MainWidget);
        if (MyMainWidget)
        {
            MyMainWidget->UpdateHealthBar(Health);
            MyMainWidget->UpdateAmmoCount(Ammo);
        }
    }
}

HUD를 활용한 고급 기능 구현

 조준점

void AMyHUD::DrawCrosshair()
{
    float CrosshairSize = 10.0f;
    float ScreenCenterX = Canvas->SizeX / 2;
    float ScreenCenterY = Canvas->SizeY / 2;
 
    DrawLine(ScreenCenterX - CrosshairSize, ScreenCenterY, ScreenCenterX + CrosshairSize, ScreenCenterY, FLinearColor::Green, 2.0f);
    DrawLine(ScreenCenterX, ScreenCenterY - CrosshairSize, ScreenCenterX, ScreenCenterY + CrosshairSize, FLinearColor::Green, 2.0f);
}

 미니맵

 미니맵은 주로 UMG 위젯을 사용하여 구현하지만, HUD에서 직접 그릴 수도 있습니다.

void AMyHUD::DrawMinimap()
{
    float MinimapSize = 200.0f;
    float MinimapX = Canvas->SizeX - MinimapSize - 20.0f;
    float MinimapY = 20.0f;
 
    DrawRect(FLinearColor(0, 0, 0, 0.5f), MinimapX, MinimapY, MinimapSize, MinimapSize);
 
    // 플레이어와 적의 위치를 미니맵에 표시
    // ...
}

 데미지 표시기

void AMyHUD::ShowDamageDirection(FVector DamageLocation)
{
    APlayerController* PC = GetOwningPlayerController();
    if (PC)
    {
        FVector PlayerLocation = PC->GetPawn()->GetActorLocation();
        FVector DamageDirection = (DamageLocation - PlayerLocation).GetSafeNormal();
 
        float Angle = FMath::Atan2(DamageDirection.Y, DamageDirection.X);
        float ScreenAngle = Angle * (180.0f / PI) + 90.0f;
 
        float IndicatorSize = 50.0f;
        float ScreenCenterX = Canvas->SizeX / 2;
        float ScreenCenterY = Canvas->SizeY / 2;
        float IndicatorDistance = FMath::Min(ScreenCenterX, ScreenCenterY) * 0.8f;
 
        FVector2D IndicatorPos(
            ScreenCenterX + IndicatorDistance * FMath::Cos(Angle),
            ScreenCenterY + IndicatorDistance * FMath::Sin(Angle)
        );
 
        DrawText(TEXT(">"), FLinearColor::Red, IndicatorPos.X, IndicatorPos.Y, nullptr, 2.0f, ScreenAngle);
    }
}

HUD의 성능 최적화 전략

  1. 캐싱 : 자주 사용하는 값들을 캐싱하여 매 프레임마다의 계산을 줄입니다.
  2. 드로우 콜 최소화 : 가능한 한 드로우 콜을 줄이도록 HUD 요소를 설계합니다.
  3. 클리핑 : 화면 밖의 요소는 그리지 않도록 합니다.
void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    // 성능 최적화를 위한 캐싱
    static float CachedScreenWidth = Canvas->SizeX;
    static float CachedScreenHeight = Canvas->SizeY;
 
    // 화면 크기가 변경되었을 때만 업데이트
    if (Canvas->SizeX != CachedScreenWidth || Canvas->SizeY != CachedScreenHeight)
    {
        CachedScreenWidth = Canvas->SizeX;
        CachedScreenHeight = Canvas->SizeY;
        // 화면 크기에 따른 계산 수행
        // ...
    }
 
    // 클리핑을 이용한 최적화
    FVector2D PlayerPos = GetPlayerScreenPosition();
    if (PlayerPos.X >= 0 && PlayerPos.X <= CachedScreenWidth &&
        PlayerPos.Y >= 0 && PlayerPos.Y <= CachedScreenHeight)
    {
        DrawPlayerIndicator(PlayerPos);
    }
}

다양한 화면 해상도에 대응하는 HUD 디자인

void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    float ScreenWidth = Canvas->SizeX;
    float ScreenHeight = Canvas->SizeY;
    float ScaleFactor = FMath::Min(ScreenWidth / 1920.0f, ScreenHeight / 1080.0f);
 
    // 해상도에 따라 스케일 조정
    float ScaledSize = 100.0f * ScaleFactor;
    FVector2D Position(ScreenWidth - ScaledSize - 20.0f, 20.0f);
 
    DrawTexture(SomeTexture, Position.X, Position.Y, ScaledSize, ScaledSize, 0, 0, 1, 1);
}

HUD와 게임플레이 시스템의 효과적인 연동

 게임플레이 이벤트에 따라 HUD를 업데이트하기 위해 델리게이트를 사용할 수 있습니다.

// AMyPlayerController.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedSignature, float, NewHealth);
 
UCLASS()
class MYPROJECT_API AMyPlayerController : public APlayerController
{
    GENERATED_BODY()
 
public:
    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnHealthChangedSignature OnHealthChanged;
 
    // ...
};
 
// AMyHUD.cpp
void AMyHUD::BeginPlay()
{
    Super::BeginPlay();
 
    AMyPlayerController* PC = Cast<AMyPlayerController>(GetOwningPlayerController());
    if (PC)
    {
        PC->OnHealthChanged.AddDynamic(this, &AMyHUD::OnHealthChanged);
    }
}
 
void AMyHUD::OnHealthChanged(float NewHealth)
{
    // HUD 업데이트 로직
}

디버깅 팁

  1. 화면에 디버그 정보 표시
void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    ##if !UE_BUILD_SHIPPING
    // 디버그 모드에서만 표시
    DrawText(FString::Printf(TEXT("Debug: FPS: %.2f"), GetWorld()->GetFPS()), FLinearColor::Yellow, 10, 10);
    ##endif
}
  1. 로깅 사용
void AMyHUD::SomeFunction()
{
    UE_LOG(LogTemp, Warning, TEXT("SomeFunction called with value: %d"), SomeValue);
}
  1. 비주얼 디버깅
void AMyHUD::DrawHUD()
{
    Super::DrawHUD();
 
    ##if !UE_BUILD_SHIPPING
    // HUD 요소의 경계를 시각화
    DrawRect(FLinearColor::Red, 100, 100, 200, 100);
    ##endif
}

 HUD 클래스는 게임의 시각적 정보를 플레이어에게 효과적으로 전달하는 중요한 역할을 합니다. C++에서 AHUD 클래스를 활용하면 높은 수준의 커스터마이징과 최적화가 가능합니다. UMG 위젯과의 통합, 동적 HUD 요소 관리, 그리고 게임플레이 시스템과의 연동을 통해 풍부하고 반응성 높은 사용자 인터페이스를 구현할 수 있습니다. 성능 최적화와 다양한 해상도 대응을 고려하여 HUD를 설계하면, 모든 플레이어에게 일관된 경험을 제공할 수 있습니다.