UI 입력 처리 및 애니메이션
PlayerController의 입력 모드를 전환하고 위젯 이벤트와 UMG 애니메이션으로 상호작용을 완성합니다.
이전 절에서 UMG의 데이터 바인딩과 HUD 시스템을 통해 게임 데이터를 UI에 표시하는 방법을 알아보았습니다.
이제 플레이어의 입력(Input)을 받아 UI와 상호작용하고, UI에 애니메이션을 적용하여 시각적 피드백과 생동감을 더하는 방법에 대해 살펴보겠습니다.
UI 입력 처리: 플레이어와의 상호작용
게임에서 UI는 단순히 정보를 보여주는 것을 넘어, 플레이어의 클릭, 마우스 오버, 키 입력 등을 받아 반응해야 합니다.
언리얼 엔진은 플레이어 컨트롤러(Player Controller)를 통해 UI 입력을 관리합니다.
입력 모드(Input Mode) 변경
가장 중요한 개념은 입력 모드입니다.
언리얼 엔진은 어떤 종류의 입력을 받아들일지 세 가지 주요 모드를 제공합니다.
세 입력 모드가 입력을 전달하는 대상과 커서·포커스의 책임을 구분합니다.
| 모드 | 입력 전달 | 별도로 정할 것 |
|---|---|---|
| GameOnly | 플레이어 입력·컨트롤러 | 커서 숨김 여부와 게임 뷰포트 복귀 |
| UIOnly | UI만 처리 | 포커스 가능한 대상과 UI 안의 닫기 경로 |
| GameAndUI | UI가 먼저 처리하며 미처리 입력은 플레이어 쪽에 전달 가능 | 같은 입력이 어느 쪽에서 소비되는지와 마우스 캡처 정책 |
- GameOnly
- 입력 전달: 플레이어 입력·컨트롤러별도로 정할 것: 커서 숨김 여부와 게임 뷰포트 복귀
- UIOnly
- 입력 전달: UI만 처리별도로 정할 것: 포커스 가능한 대상과 UI 안의 닫기 경로
- GameAndUI
- 입력 전달: UI가 먼저 처리하며 미처리 입력은 플레이어 쪽에 전달 가능별도로 정할 것: 같은 입력이 어느 쪽에서 소비되는지와 마우스 캡처 정책
bShowMouseCursor는 모드와 별도로 설정합니다. UIOnly가 월드 시뮬레이션을 멈추거나, GameAndUI가 모든 입력을 양쪽에 복제하는 것은 아닙니다.
APlayerController 클래스에서 입력 모드를 설정합니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"
class UUserWidget;
UCLASS()
class MYPROJECT_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
// 인벤토리 UI를 열고 닫는 함수
UFUNCTION(BlueprintCallable, Category = "UI")
void ToggleInventoryUI();
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
TSubclassOf<UUserWidget> InventoryWidgetClass;
UPROPERTY()
UUserWidget* CurrentInventoryWidget;
};#include "MyPlayerController.h"
#include "Blueprint/UserWidget.h"
void AMyPlayerController::ToggleInventoryUI()
{
if (!IsLocalController()) return;
if (CurrentInventoryWidget && CurrentInventoryWidget->IsInViewport())
{
// 인벤토리 UI가 열려 있다면 닫기
CurrentInventoryWidget->RemoveFromParent();
CurrentInventoryWidget = nullptr;
// 게임 입력 모드로 전환
FInputModeGameOnly InputMode;
SetInputMode(InputMode);
bShowMouseCursor = false; // 마우스 커서 숨기기
UE_LOG(LogTemp, Warning, TEXT("Inventory UI Closed. Game Input Mode."));
}
else
{
// 인벤토리 UI가 닫혀 있다면 열기
if (InventoryWidgetClass)
{
CurrentInventoryWidget = CreateWidget<UUserWidget>(this, InventoryWidgetClass);
if (CurrentInventoryWidget)
{
CurrentInventoryWidget->AddToViewport();
// UI 입력 모드로 전환
FInputModeUIOnly InputMode;
InputMode.SetWidgetToFocus(CurrentInventoryWidget->TakeWidget()); // 포커스할 위젯 지정 (선택 사항)
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); // 마우스 커서 뷰포트 잠금 해제 (선택 사항)
SetInputMode(InputMode);
bShowMouseCursor = true; // 마우스 커서 표시
UE_LOG(LogTemp, Warning, TEXT("Inventory UI Opened. UI Input Mode."));
}
}
}
}SetWidgetToFocus: FInputModeUIOnly를 사용할 때, 키보드 포커스를 받을 위젯을 지정합니다. 대상이 포커스를 받을 수 있어야 하므로 이 예제의 인벤토리 루트에는 Is Focusable을 켜거나 실제 버튼·입력 위젯을 지정합니다.
인벤토리의 닫기 버튼이나 포커스를 받은 위젯의 키 처리에서 소유 컨트롤러의 ToggleInventoryUI를 호출하도록 연결합니다. UIOnly가 된 뒤 게임 입력 바인딩만으로 닫는 경로는 충분하지 않습니다. 입력 모드 전환 자체가 게임을 일시정지시키지는 않습니다.
위젯 이벤트 처리
버튼 클릭, 슬라이더 값 변경 등 사용자 상호작용은 UMG 디자이너의 그래프 탭에서 블루프린트 이벤트 노드를 통해 처리하는 것이 일반적입니다.
하지만 C++에서도 이벤트에 직접 함수를 바인딩할 수 있습니다.
C++에서 버튼 클릭 이벤트 처리 예시#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MyPlayerHUD.generated.h"
class UButton; // UButton을 사용하기 위해 포함
UCLASS()
class MYPROJECT_API UMyPlayerHUD : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(meta = (BindWidget))
UButton* AttackButton; // UMG 디자이너에서 이 버튼의 이름도 'AttackButton'으로 설정하고 Is Variable 체크
protected:
virtual void NativeOnInitialized() override;
// 버튼 클릭 이벤트에 바인딩될 함수
UFUNCTION()
void OnAttackButtonClicked(); // UFUNCTION으로 선언되어야 함
};#include "MyPlayerHUD.h"
#include "Components/Button.h" // UButton 헤더
void UMyPlayerHUD::NativeOnInitialized()
{
Super::NativeOnInitialized();
// AttackButton이 유효한지 확인하고 클릭 이벤트에 함수 바인딩
if (AttackButton)
{
// AddDynamic: 델리게이트에 C++ 함수 바인딩
AttackButton->OnClicked.AddDynamic(this, &UMyPlayerHUD::OnAttackButtonClicked);
}
}
void UMyPlayerHUD::OnAttackButtonClicked()
{
UE_LOG(LogTemp, Warning, TEXT("Attack Button Clicked!"));
// 여기에 공격 로직 또는 플레이어 컨트롤러에 공격 명령 전달 로직 구현
// 예: AMyPlayerController* PC = Cast<AMyPlayerController>(GetOwningPlayer());
// if (PC) { PC->PerformAttack(); }
}이 방식은 특정 위젯의 이벤트를 해당 위젯 C++ 클래스에서 직접 처리할 때 유용합니다.
복잡한 게임플레이 로직은 여전히 플레이어 컨트롤러나 폰(Pawn)/캐릭터에서 처리하는 것이 일반적이므로, UI 위젯은 이벤트를 플레이어 컨트롤러 등으로 다시 전달하는 역할을 할 수 있습니다.
버튼 예제는 NativeOnInitialized에서 한 번 구독합니다. NativeConstruct마다 구독을 추가하면 화면 재구성 때 중복될 수 있습니다.
UI 애니메이션: 시각적 피드백과 생동감
UMG는 위젯의 속성(위치, 크기, 투명도, 회전 등)을 시간에 따라 변화시키는 강력한 애니메이션 시스템을 내장하고 있습니다.
이는 UI에 생동감과 시각적 피드백을 제공하여 사용자 경험을 향상시킵니다.
UMG 디자이너에서 애니메이션 생성
애니메이션 탭 열기: UMG UI 디자이너의 Designer 탭에서 화면 하단의 Animations 패널을 찾습니다.
새 애니메이션 추가: + Animation 버튼을 클릭하여 새 애니메이션을 생성하고 이름을 지정합니다 (예: FadeIn, ButtonPress).
트랙 추가: 애니메이션을 만들 위젯을 선택하고, + Track 버튼을 클릭하여 해당 위젯을 애니메이션에 추가합니다.
위젯의 Transform (위치, 회전, 크기), Render Opacity (투명도), Color and Opacity (색상), Render Transform의 Angle, 지원되는 진행률 바 속성 등 다양한 속성을 애니메이션화할 수 있습니다.
- 타임라인에서 원하는 시간 지점을 선택합니다.
- 위젯의 해당 속성을 조절하고, 속성 옆의 키 아이콘을 클릭하여 키 프레임을 추가합니다.
- 다른 시간 지점으로 이동하여 속성을 변경하고 다시 키 프레임을 추가하면, 언리얼 엔진이 두 키 프레임 사이의 값을 자동으로 보간하여 애니메이션을 생성합니다.
재생 설정: 애니메이션 패널에서 Looping (반복 재생), Play Rate (재생 속도) 등을 조절할 수 있습니다.
C++에서 UMG 애니메이션 제어
UMG 애니메이션은 위젯 블루프린트 내에 존재하므로, C++에서 애니메이션을 재생하려면 해당 애니메이션에 대한 참조를 얻어야 합니다.
BindWidgetAnim 메타데이터로 디자이너의 같은 이름 애니메이션을 연결하고, 그 포인터를 PlayAnimation에 전달합니다. 아래 두 클래스 예시는 필요한 멤버와 함수를 이전 HUD 클래스에 합쳐 사용합니다.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MyPlayerHUD.generated.h"
class UWidgetAnimation; // UWidgetAnimation을 사용하기 위해 포함
UCLASS()
class MYPROJECT_API UMyPlayerHUD : public UUserWidget
{
GENERATED_BODY()
public:
// UMG 디자이너에서 만든 애니메이션을 C++에서 참조하기 위한 변수
// 디자이너의 같은 이름 애니메이션을 BindWidgetAnim으로 연결합니다.
UPROPERTY(Transient, meta = (BindWidgetAnim), BlueprintReadWrite, Category = "Animations")
UWidgetAnimation* FadeInAnimation; // 디자이너에서 'FadeInAnimation'이라는 이름으로 애니메이션을 만들었다고 가정
protected:
virtual void NativeConstruct() override;
};#include "MyPlayerHUD.h"
#include "Animation/WidgetAnimation.h" // UWidgetAnimation 헤더
void UMyPlayerHUD::NativeConstruct()
{
Super::NativeConstruct();
// BindWidgetAnim으로 연결된 애니메이션을 재생합니다.
if (FadeInAnimation)
{
// 위젯이 생성될 때 페이드인 애니메이션 재생
PlayAnimation(FadeInAnimation);
}
}
// 애니메이션 재생 예시 함수
// UFUNCTION(BlueprintCallable, Category = "UI")
// void PlayMyFadeInAnimation()
// {
// if (FadeInAnimation)
// {
// PlayAnimation(FadeInAnimation); // 기본 재생 속도와 한 번 재생
// }
// }UUserWidget)
PlayAnimation(UWidgetAnimation* InAnimation, float StartAtTime = 0.0f, int32 NumLoopsToPlay = 1, EUMGSequencePlayMode::Type PlayMode = EUMGSequencePlayMode::Forward, float PlaybackSpeed = 1.0f): 특정 애니메이션을 재생합니다.StopAnimation(UWidgetAnimation* InAnimation)/StopAllAnimations(): 애니메이션을 중지합니다.PauseAnimation(UWidgetAnimation* InAnimation): 애니메이션을 일시 정지합니다.IsAnimationPlaying(UWidgetAnimation* InAnimation): 특정 애니메이션이 현재 재생 중인지 확인합니다.
이 예제의 FadeInAnimation은 구성 시 재생되며, 공격 함수 호출·닫기 애니메이션·완료 후 제거는 구현하지 않았습니다. 종료 연출이 필요하면 완료 이벤트에 제거와 입력 복귀를 연결합니다. 모듈에는 UMG 의존성이 필요합니다.
입력 처리는 플레이어가 게임과 상호작용하는 통로를 제공하고, 애니메이션은 UI에 생동감과 시각적 피드백을 부여하여 사용자 경험을 크게 향상시킵니다.
APlayerController의 입력 모드 전환을 통해 게임 플레이와 UI 상호작용 간의 전환을 매끄럽게 하고, 위젯 이벤트 처리와 UMG 애니메이션 제어는 여러분의 UI를 더욱 풍부하고 반응적으로 만들 것입니다.