UMG 탭 그룹 위젯 (Tab Group Widget) 설계 문서
프로젝트 개요
Onepiece 프로젝트의 UMG 시스템 내에서 재사용 가능한 탭 UI를 구현합니다. 이 시스템은 세 가지 핵심 클래스로 구성됩니다:
- UTabButton: 개별 탭 버튼 위젯
- UTabIndicator: 선택된 탭을 표시하는 인디케이터 위젯
- UTabButtonGroup: 전체 탭 시스템을 관리하는 컨트롤러 위젯
사용자는 UTabButtonGroup에 탭 데이터(TArray)와 탭 버튼 클래스(TSubclassOf)를 제공하면, 전체 탭 그룹이 동적으로 생성되고 관리됩니다.
프로젝트 코딩 컨벤션
필수 참고 문서
AgentRule/Project/Onepiece/AGENT_GUIDE.md- 프로젝트 공통 가이드AgentRule/Project/Onepiece/CODING_CONVENTIONS.md- Onepiece 고유 코딩 규칙AgentRule/Project/ue_coding_conventions.md- UE 범용 코딩 컨벤션
주요 규칙
- 파일 헤더: 저작권 표시 필수
// Copyright (c) 2025 Doppleddiggong. All rights reserved. // Unauthorized copying, modification, or distribution of this file, // via any medium is strictly prohibited. Proprietary and confidential. - 변수명:
UPROPERTY는 PascalCase, 일반 변수는 camelCase, bool 타입은 bPascalCase - 포인터:
TObjectPtr<>사용 - Doxygen 주석:
/// @brief,/// @param [in/out],/// @return스타일 - API 매크로:
ONEPIECE_API사용
1. UTabButton 위젯 (개별 탭 버튼)
UTabButtonGroup에 의해 동적으로 생성되고 관리되는 개별 탭 버튼입니다.
1.1. 주요 프로퍼티
BindWidget 컴포넌트
/// @brief 클릭을 감지할 루트 버튼
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
TObjectPtr Button_Tab;
/// @brief 탭의 텍스트 라벨
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
TObjectPtr Txt_ButtonLabel;
/// @brief 탭이 선택되었을 때 활성화될 이미지
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
TObjectPtr Image_ActivateState;
내부 상태
/// @brief 이 탭 버튼을 소유한 부모 그룹
TWeakObjectPtr OwnerTabGroup;
/// @brief 이 탭 버튼의 그룹 내 인덱스
int32 TabIndex = -1;
1.2. 핵심 기능
InitData()
void InitData(int32 InTabIndex, UTabButtonGroup* InOwnerGroup);
- 탭 인덱스와 소유 그룹 저장
SetSelected()
void SetSelected(bool bIsSelected);
Image_ActivateState의 Visibility와 색상 제어Txt_ButtonLabel의 색상 제어OwnerTabGroup->GetTextColor(),GetActivateColor()사용
SetLabel()
void SetLabel(const FText& InText);
- 텍스트 라벨 설정
1.3. 이벤트
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTabButtonClicked, int32, TabIndex);
UPROPERTY(BlueprintAssignable, Category = "TabButton")
FOnTabButtonClicked OnTabButtonClicked;
2. UTabIndicator 위젯 (선택 인디케이터)
선택된 탭으로 부드럽게 이동하는 애니메이션을 제공하는 인디케이터 위젯입니다.
2.1. 주요 기능
MoveTo()
void MoveTo(FVector2D TargetPosition, bool bAnimate = true);
- 목표 위치로 이동
bAnimate = true: 부드러운 애니메이션bAnimate = false: 즉시 이동
SetAnimationSpeed()
void SetAnimationSpeed(float Speed);
- 애니메이션 속도 설정 (초 단위)
2.2. 이벤트
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMoveCompleted);
UPROPERTY(BlueprintAssignable, Category = "TabIndicator|Events")
FOnMoveCompleted OnMoveCompleted;
- 이동 애니메이션 완료 시 발생
2.3. 내부 구현
- 타이머 기반 애니메이션: ~60 FPS (0.016초 간격)
- Lerp 이동: 부드러운 보간
- CanvasPanelSlot 사용: 자유로운 위치 제어
3. UTabButtonGroup 위젯 (탭 시스템 관리자)
전체 탭 시스템을 관리하는 핵심 위젯입니다.
3.1. 데이터 프로퍼티
/// @brief 각 탭에 표시될 텍스트 라벨 배열
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Data")
TArray TabLabels;
/// @brief 탭 버튼으로 생성할 UTabButton 위젯 블루프린트 클래스
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Data")
TSubclassOf TabButtonClass;
/// @brief 기본으로 선택될 탭의 인덱스
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Data")
int32 DefaultTabIndex = 0;
3.2. 외형 프로퍼티
/// @brief 선택된 탭의 텍스트 색상
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Appearance")
FLinearColor TextSelectColor = FLinearColor::White;
/// @brief 비선택된 탭의 텍스트 색상
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Appearance")
FLinearColor TextUnselectColor = FLinearColor(0.5f, 0.5f, 0.5f);
/// @brief 선택된 탭의 활성화 이미지 색상
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Appearance")
FLinearColor ActivateSelectColor = FLinearColor::White;
/// @brief 비선택된 탭의 활성화 이미지 색상
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Appearance")
FLinearColor ActivateUnselectColor = FLinearColor(0.5f, 0.5f, 0.5f);
3.3. 레이아웃 프로퍼티
/// @brief 각 탭의 고정 크기 (Width × Height)
/// TabIndicator 크기도 자동으로 이 값으로 설정됩니다.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Layout")
FVector2D TabSize = FVector2D(300.0f, 75.0f);
중요: TabSize 하나만 설정하면:
- TabIndicator 크기 자동 설정
- 위치 계산:
X = TabSize.X * TabIndex
3.4. 애니메이션 프로퍼티
/// @brief 선택 인디케이터의 이동 애니메이션 활성화 여부
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Animation")
bool bAnimateIndicator = true;
/// @brief 선택 인디케이터의 애니메이션 속도 (초 단위)
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TabButtonGroup|Animation", meta=(EditCondition="bAnimateIndicator"))
float IndicatorAnimationSpeed = 0.15f;
3.5. BindWidget 컴포넌트
/// @brief 생성된 탭 버튼들이 추가될 컨테이너
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
TObjectPtr TabContainer;
/// @brief 선택된 탭을 따라 움직이는 인디케이터 위젯
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
TObjectPtr TabIndicator;
3.6. 핵심 기능
ApplyTab()
void ApplyTab();
TabLabels데이터를 기반으로 탭 버튼들을 동적 생성- 기존 탭 제거 → 새 탭 생성 →
TabContainer에 추가 HorizontalBoxSlot설정:HAlign_Fill,VAlign_Fill
OnSelectTab()
void OnSelectTab(int32 TabIndex, bool bBroadcastEvent = true);
- 특정 인덱스의 탭을 선택
- 모든 탭 버튼의 선택 상태 업데이트
- 인디케이터 위치 업데이트
bBroadcastEvent = true:OnTabSelected이벤트 발생
UpdateIndicatorPosition()
void UpdateIndicatorPosition();
- 고정 폭 기반 위치 계산:
X = TabSize.X * CurTabIndex TabIndicator->MoveTo(targetPosition, bAnimateIndicator)호출
3.7. 이벤트
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTabSelected, int32, TabIndex);
UPROPERTY(BlueprintAssignable, Category = "TabButtonGroup|Events")
FOnTabSelected OnTabSelected;
4. 블루프린트 구조
4.1. WBP_TabButton (탭 버튼 위젯)
부모 클래스: UTabButton
계층 구조:
CanvasPanel (루트)
├─ Button (이름: Button_Tab)
├─ Image (이름: Image_ActivateState)
└─ TextBlock (이름: Txt_ButtonLabel)
권장 크기: 300 × 75 (TabSize와 일치)
4.2. WBP_TabIndicator (인디케이터 위젯)
부모 클래스: UTabIndicator
계층 구조:
CanvasPanel (루트)
└─ Image (인디케이터 이미지)
주의: 크기는 자동 설정되므로 블루프린트에서 설정하지 않아도 됨
4.3. WBP_TabButtonGroup (탭 그룹 위젯)
부모 클래스: UTabButtonGroup
권장 계층 구조 (Overlay 방식):
Overlay (이름: Overlay_Root)
├─ TabContainer (HorizontalBox)
│ └─ [동적 생성되는 TabButton들]
└─ Canvas Panel (이름: Canvas_Indicator)
└─ TabIndicator
Overlay 설정:
- Overlay_Root: Fill (0, 0, 1, 1)
- TabContainer: Horizontal/Vertical Alignment = Fill
- Canvas_Indicator: Horizontal/Vertical Alignment = Fill (TabContainer와 같은 공간)
TabIndicator 설정:
- Anchors: (0, 0, 0, 0) - 왼쪽 위 고정
- Position: (0, 0)
- Size: 자동 설정됨 (
TabSize로)
5. 사용 방법
5.1. 기본 설정
- WBP_TabButtonGroup를 메뉴에 추가
- 디테일 패널에서 설정:
Tab Labels: ["캐릭터", "인벤토리", "설정"] Tab Button Class: WBP_TabButton Default Tab Index: 0 TabSize: (300, 75)
5.2. WidgetSwitcher 연동
블루프린트 그래프:
[TabButtonGroup] OnTabSelected
↓
[TabIndex]
↓
[WidgetSwitcher] Set Active Widget Index
5.3. 코드 예시 (C++)
// 특정 탭 선택 (이벤트 발생)
TabButtonGroup->OnSelectTab(1, true);
// 특정 탭 선택 (이벤트 발생 안 함)
TabButtonGroup->OnSelectTab(2, false);
6. 핵심 특징
6.1. 단순화된 위치 계산
- 고정 폭 기반: 복잡한 지오메트리 계산 불필요
- 공식:
X = TabSize.X * TabIndex - 예시:
- 0번 탭: X = 300 × 0 = 0
- 1번 탭: X = 300 × 1 = 300
- 2번 탭: X = 300 × 2 = 600
6.2. 자동 크기 설정
TabSize하나만 변경하면TabIndicator크기도 자동 변경NativeConstruct()에서CanvasPanelSlot::SetSize(TabSize)호출
6.3. 책임 분리
- UTabButton: 개별 탭의 외형과 클릭 처리
- UTabIndicator: 이동 애니메이션 처리
- UTabButtonGroup: 전체 탭 시스템 관리
6.4. 이벤트 기반 확장
OnMoveCompleted: 인디케이터 이동 완료 시 (사운드, 이펙트 등)OnTabSelected: 탭 선택 변경 시 (컨텐츠 전환 등)
7. 구현 체크리스트
코드
- [x] 저작권 헤더 추가
- [x]
TObjectPtr<>사용 - [x] Doxygen 주석 적용
- [x]
ONEPIECE_API매크로 사용 - [x] 변수명 컨벤션 준수
기능
- [x] 탭 동적 생성/제거
- [x] 탭 선택 상태 관리
- [x] 인디케이터 이동 애니메이션
- [x] 고정 폭 기반 위치 계산
- [x] TabSize 자동 적용
- [x] 색상 제어 (텍스트 + 활성화 이미지)
블루프린트
- [x]
BindWidget이름 규칙 준수 - [x] Overlay 구조 적용
- [x] 에디터 프리뷰 동작 확인
이벤트
- [x]
OnTabSelected델리게이트 - [x]
OnMoveCompleted델리게이트 - [x]
OnTabButtonClicked델리게이트
8. 고급 활용
8.1. 커스텀 인디케이터 애니메이션
OnMoveCompleted 이벤트를 활용:
TabIndicator->OnMoveCompleted.AddDynamic(this, &UMyWidget::OnIndicatorMoved);
void UMyWidget::OnIndicatorMoved()
{
// 사운드 재생
// 파티클 이펙트
// 햅틱 피드백
}
8.2. 동적 탭 추가/제거
// 탭 추가
TabLabels.Add(FText::FromString("새 탭"));
ApplyTab();
// 탭 제거
TabLabels.RemoveAt(1);
ApplyTab();
8.3. 커스텀 탭 크기
// 탭 크기 변경
TabSize = FVector2D(400.0f, 100.0f);
// NativeConstruct()에서 자동으로 TabIndicator 크기 업데이트됨
9. 문제 해결
인디케이터 위치가 안 맞을 때
Overlay 구조 확인:
- TabContainer와 Canvas_Indicator가 같은 Overlay의 자식인지 확인
- 둘 다 Fill Alignment인지 확인
TabIndicator Anchors 확인:
- Anchors: (0, 0, 0, 0) - 왼쪽 위 고정
TabSize 확인:
- TabButton 크기와 TabSize가 일치하는지 확인
탭이 생성되지 않을 때
- TabButtonClass 설정 확인
- TabLabels 배열이 비어있지 않은지 확인
- BindWidget 이름 확인:
TabContainer정확히 일치
10. 참고 파일
소스 파일
Source/Onepiece/WidgetComponents/Public/UTabButton.hSource/Onepiece/WidgetComponents/Private/UTabButton.cppSource/Onepiece/WidgetComponents/Public/UTabIndicator.hSource/Onepiece/WidgetComponents/Private/UTabIndicator.cppSource/Onepiece/WidgetComponents/Public/UTabButtonGroup.hSource/Onepiece/WidgetComponents/Private/UTabButtonGroup.cpp
블루프린트 예시
Content/CustomContents/UI/Components/WBP_TabButton.uassetContent/CustomContents/UI/Components/WBP_TabIndicator.uasset(권장)Content/CustomContents/UI/Components/WBP_TabButtonGroup.uasset