UMG (Unreal Motion Graphics) 소개
UMG (Unreal Motion Graphics)는 언리얼 엔진의 강력한 UI 시스템으로, 게임 내 사용자 인터페이스를 쉽고 효율적으로 제작할 수 있게 해줍니다.
이 절에서는 UMG의 기본 개념과 C++에서의 활용 방법을 살펴보겠습니다.
UMG의 주요 컴포넌트
- 위젯 (Widget) : UI의 기본 구성 요소
- 캔버스 패널 (Canvas Panel) : 위젯을 자유롭게 배치할 수 있는 컨테이너
- 앵커 (Anchor) : 위젯의 위치와 크기를 상대적으로 지정하는 기준점
C++에서 기본적인 UI 요소 생성 및 조작
위젯 생성
UCLASS()
class MYPROJECT_API UMyUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
private:
UPROPERTY(meta = (BindWidget))
class UButton* MyButton;
UPROPERTY(meta = (BindWidget))
class UTextBlock* MyTextBlock;
UFUNCTION()
void OnButtonClicked();
};
void UMyUserWidget::NativeConstruct()
{
Super::NativeConstruct();
if (MyButton)
{
MyButton->OnClicked.AddDynamic(this, &UMyUserWidget::OnButtonClicked);
}
}
void UMyUserWidget::OnButtonClicked()
{
if (MyTextBlock)
{
MyTextBlock->SetText(FText::FromString("Button Clicked!"));
}
}
위젯 생성 및 화면에 추가
UCLASS()
class MYPROJECT_API AMyHUD : public AHUD
{
GENERATED_BODY()
public:
void ShowMyWidget();
private:
UPROPERTY()
UMyUserWidget* MyWidget;
};
void AMyHUD::ShowMyWidget()
{
if (MyWidget == nullptr)
{
MyWidget = CreateWidget<UMyUserWidget>(GetOwningPlayerController(), UMyUserWidget::StaticClass());
}
if (MyWidget)
{
MyWidget->AddToViewport();
}
}
위젯 블루프린트와 C++ 코드 연동
위젯 블루프린트에서 C++ 함수를 호출하거나 C++에서 위젯 블루프린트의 속성에 접근할 수 있습니다.
UCLASS()
class MYPROJECT_API UMyWidgetBlueprintBase : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "UI")
void UpdateHealthBar(float Health);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")
float MaxHealth = 100.0f;
};
void UMyWidgetBlueprintBase::UpdateHealthBar(float Health)
{
// 헬스바 업데이트 로직
}
동적 UI 생성 및 관리
동적으로 UI 요소를 생성하고 관리하는 방법
UCLASS()
class MYPROJECT_API UDynamicUIManager : public UObject
{
GENERATED_BODY()
public:
void CreateDynamicUI(TSubclassOf<UUserWidget> WidgetClass);
void RemoveDynamicUI(UUserWidget* WidgetToRemove);
private:
UPROPERTY()
TArray<UUserWidget*> ActiveWidgets;
};
void UDynamicUIManager::CreateDynamicUI(TSubclassOf<UUserWidget> WidgetClass)
{
APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
if (PlayerController)
{
UUserWidget* NewWidget = CreateWidget<UUserWidget>(PlayerController, WidgetClass);
if (NewWidget)
{
NewWidget->AddToViewport();
ActiveWidgets.Add(NewWidget);
}
}
}
void UDynamicUIManager::RemoveDynamicUI(UUserWidget* WidgetToRemove)
{
if (WidgetToRemove)
{
WidgetToRemove->RemoveFromParent();
ActiveWidgets.Remove(WidgetToRemove);
}
}
UI 애니메이션 구현
UMG에서 C++를 통해 UI 애니메이션을 구현하는 방법
UCLASS()
class MYPROJECT_API UAnimatedWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void PlayFadeAnimation();
private:
UPROPERTY(Transient, meta = (BindWidgetAnim))
UWidgetAnimation* FadeAnimation;
};
void UAnimatedWidget::NativeConstruct()
{
Super::NativeConstruct();
if (FadeAnimation)
{
PlayAnimation(FadeAnimation);
}
}
void UAnimatedWidget::PlayFadeAnimation()
{
PlayAnimation(FadeAnimation);
}
UMG 성능 최적화 전략
- 위젯 풀링
UCLASS()
class MYPROJECT_API UWidgetPool : public UObject
{
GENERATED_BODY()
public:
UUserWidget* GetWidget(TSubclassOf<UUserWidget> WidgetClass);
void ReturnWidget(UUserWidget* Widget);
private:
TMap<TSubclassOf<UUserWidget>, TArray<UUserWidget*>> PooledWidgets;
};
UUserWidget* UWidgetPool::GetWidget(TSubclassOf<UUserWidget> WidgetClass)
{
if (PooledWidgets.Contains(WidgetClass) && PooledWidgets[WidgetClass].Num() > 0)
{
return PooledWidgets[WidgetClass].Pop();
}
else
{
return CreateWidget<UUserWidget>(GetWorld(), WidgetClass);
}
}
void UWidgetPool::ReturnWidget(UUserWidget* Widget)
{
if (Widget)
{
Widget->RemoveFromParent();
PooledWidgets.FindOrAdd(Widget->GetClass()).Add(Widget);
}
}
- 레이아웃 캐싱
UCLASS()
class MYPROJECT_API UOptimizedUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
protected:
virtual void NativePreConstruct() override;
private:
UPROPERTY()
FGeometry CachedGeometry;
void UpdateCachedGeometry();
};
void UOptimizedUserWidget::NativePreConstruct()
{
Super::NativePreConstruct();
UpdateCachedGeometry();
}
void UOptimizedUserWidget::UpdateCachedGeometry()
{
// 레이아웃 계산 및 CachedGeometry 업데이트
}
반응형 UI 디자인 구현
다양한 해상도와 화면 비율에 대응하는 반응형 UI 설계
UCLASS()
class MYPROJECT_API UResponsiveWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
protected:
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
private:
void UpdateLayout();
};
void UResponsiveWidget::NativeConstruct()
{
Super::NativeConstruct();
UpdateLayout();
}
void UResponsiveWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if (MyGeometry.GetLocalSize() != LastSize)
{
LastSize = MyGeometry.GetLocalSize();
UpdateLayout();
}
}
void UResponsiveWidget::UpdateLayout()
{
// 화면 크기에 따라 위젯 레이아웃 조정
}
고급 UI 기능 구현
인벤토리 시스템
UCLASS()
class MYPROJECT_API UInventoryWidget : public UUserWidget
{
GENERATED_BODY()
public:
void UpdateInventory(const TArray<FItemData>& Items);
private:
UPROPERTY(meta = (BindWidget))
class UUniformGridPanel* ItemGrid;
UPROPERTY(EditDefaultsOnly, Category = "UI")
TSubclassOf<UUserWidget> ItemWidgetClass;
};
void UInventoryWidget::UpdateInventory(const TArray<FItemData>& Items)
{
ItemGrid->ClearChildren();
for (const FItemData& Item : Items)
{
UUserWidget* ItemWidget = CreateWidget<UUserWidget>(this, ItemWidgetClass);
// ItemWidget 설정
ItemGrid->AddChildToUniformGrid(ItemWidget);
}
}
미니맵
UCLASS()
class MYPROJECT_API UMinimapWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
private:
UPROPERTY(meta = (BindWidget))
class UImage* MinimapImage;
void UpdateMinimapPosition();
};
void UMinimapWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
UpdateMinimapPosition();
}
void UMinimapWidget::UpdateMinimapPosition()
{
// 플레이어 위치에 따라 미니맵 이미지 업데이트
}
대화 시스템
UCLASS()
class MYPROJECT_API UDialogueWidget : public UUserWidget
{
GENERATED_BODY()
public:
void StartDialogue(const TArray<FString>& DialogueLines);
private:
UPROPERTY(meta = (BindWidget))
class UTextBlock* DialogueText;
UPROPERTY(meta = (BindWidget))
class UButton* NextButton;
UFUNCTION()
void OnNextButtonClicked();
TArray<FString> CurrentDialogue;
int32 CurrentLineIndex;
};
void UDialogueWidget::StartDialogue(const TArray<FString>& DialogueLines)
{
CurrentDialogue = DialogueLines;
CurrentLineIndex = 0;
if (DialogueText && CurrentDialogue.Num() > 0)
{
DialogueText->SetText(FText::FromString(CurrentDialogue[CurrentLineIndex]));
}
}
void UDialogueWidget::OnNextButtonClicked()
{
CurrentLineIndex++;
if (CurrentLineIndex < CurrentDialogue.Num())
{
DialogueText->SetText(FText::FromString(CurrentDialogue[CurrentLineIndex]));
}
else
{
RemoveFromParent();
}
}
UMG는 언리얼 엔진에서 강력하고 유연한 UI 시스템을 구축할 수 있게 해주는 도구입니다.
C++를 사용하여 UMG의 기능을 확장하고 커스터마이즈함으로써, 게임의 요구사항에 맞는 복잡하고 인터랙티브한 UI를 구현할 수 있습니다.
위젯 블루프린트와 C++ 코드를 효과적으로 연동하여 사용하면, 디자이너와 프로그래머 간의 협업을 원활히 하고 개발 효율성을 높일 수 있습니다.
동적 UI 생성 및 관리 기법을 통해 게임 상황에 따라 유동적으로 변화하는 UI를 구현할 수 있으며, UI 애니메이션을 활용하여 더욱 생동감 있는 인터페이스를 만들 수 있습니다.