[UE] Slate 문법 정리

네,가능합니다 ㅣ 2025. 4. 23. 16:30

1. Slate 서술형 문법 개요

Slate UI는 C++ 코드 안에서 위젯 트리를 함수 체이닝으로 선언하는 방식으로 구성함. 포인터 간접 접근(인디렉션) 없이도 위젯 레퍼런스를 곧바로 얻어 즉시 사용하거나 속성을 바인딩할 수 있음. 결과적으로 코드 가독성과 런타임 안정성 향상 효과를 가짐

// 인자 정의 예시
SLATE_BEGIN_ARGS(SSubMenuButton)
    : _ShouldAppearHovered(false) {}
    SLATE_ATTRIBUTE(FString, Label)              // 버튼 라벨
    SLATE_EVENT(FOnClicked, OnClicked)           // 클릭 델리게이트
    SLATE_NAMED_SLOT(FArguments, FSimpleSlot, Content) // 내부 콘텐츠
    SLATE_ATTRIBUTE(bool, ShouldAppearHovered)   // 호버 외관 여부
SLATE_END_ARGS()

2. 컴포지션 (Composition)

컴포지션은 SNew 매크로와 슬롯 대괄호 ([]) 블록을 이용해 부모-자식 구조를 선언적으로 작성하는 방법. 모든 자식 위젯은 부모의 슬롯 안에 정의되며, 중첩을 통해 복잡한 UI 트리를 구성함

TSharedRef<SSubMenuButton> SubMenuButton =
    SNew(SSubMenuButton)
    .Label(FString(TEXT("서브 메뉴")))
    .OnClicked(this, &SMyWidget::HandleSubMenuClicked)
    .ShouldAppearHovered(true)
    [
        SNew(STextBlock)
        .Text(FText::FromString(TEXT("내용")))
    ];

3. 스타일 (Style)

위젯별 속성 메서드(예: .ForegroundColor(), .Font())로 즉석에서 스타일을 지정하거나, FSlateStyleSet을 전역 등록해 재사용 가능한 스타일 리소스를 정의함. 스타일은 위젯 장면 변경 없이도 스킨을 교체하는 패턴을 구현할 수 있음

SubMenuButton->SetForegroundColor(FLinearColor::Yellow);

4. 입력 (Input)

Slate 위젯은 델리게이트(예: FOnClicked, FOnKeyDown)를 통해 입력을 수신함. 델리게이트는 FReply 값을 반환해 Propagate 여부를 제어함. 또한 Enhanced Input 또는 Low-Level 키보드 루트를 통해 핫키 바인딩 가능

FReply SMyWidget::HandleSubMenuClicked()
{
    DoSomething();
    return FReply::Handled(); // 이벤트 소비
}

5. 출력 (Binding & Invalidate)

SLATE_ATTRIBUTE 로 선언한 속성은 람다 또는 TAttribute 바인딩을 통해 실시간으로 값을 제공함. 속성이 변경될 때 Invalidate(EInvalidateWidget::Layout) 또는 자동 Dirty 추적을 통해 랭더링을 재계산함

.Label(this, &SMyWidget::GetDynamicLabel)   // 동적 라벨

6. 레이아웃 프리미티브 (Layout Primitives)

  • SVerticalBox & SHorizontalBox - 박스 슬롯 에 .AutoHeight(), .FillWidth() 등 비율 제어 가능함
  • SOverlay - 위젯 겹치기 레이어 배치에 사용함
  • SBox - 고정 Size 또는 Padding 지정에 편리함
  • SUniformGridPanel - 표형 균등 그리드 구성 도움됨

7. 사용자 주도형 레이아웃

드래그 리사이즈, 스플리터, 앵커링 등을 통해 플레이어가 직접 패널을 배치할 수 있도록 SConstraintCanvas, SSplitter를 활용함. 상태 저장은 JSON 또는 INI 에 저장해 재시작 후 복원 작업 권장함

SAssignNew(Root, SSplitter)
+ SSplitter::Slot()
  .Value(0.3f)
  [
      LeftPanel.ToSharedRef()
  ]
+ SSplitter::Slot()
  .Value(0.7f)
  [
      RightPanel.ToSharedRef()
  ];

8. 전체 코드 스니펫 Workflow

아래 예시는 커스텀 버튼 클래스 구현부터 위젯 생성, 스타일 적용, 입력 바인딩을 통합 시연함

// 1) 헤더
class SSubMenuButton : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SSubMenuButton)
        : _ShouldAppearHovered(false) {}
        SLATE_ATTRIBUTE(FString, Label)
        SLATE_EVENT(FOnClicked, OnClicked)
        SLATE_NAMED_SLOT(FArguments, FSimpleSlot, Content)
        SLATE_ATTRIBUTE(bool, ShouldAppearHovered)
    SLATE_END_ARGS()

    void Construct(const FArguments& InArgs);

private:
    FReply HandleClick();
    TAttribute<FString> Label;
    bool bForceHover;
};

// 2) CPP
void SSubMenuButton::Construct(const FArguments& InArgs)
{
    Label        = InArgs._Label;
    OnClicked    = InArgs._OnClicked;
    bForceHover  = InArgs._ShouldAppearHovered;

    ChildSlot
    [
        SNew(SButton)
        .OnClicked(this, &SSubMenuButton::HandleClick)
        [
            SNew(STextBlock)
            .Text_Lambda([this]{ return FText::FromString(Label.Get()); })
        ]
    ];
}

FReply SSubMenuButton::HandleClick()
{
    return OnClicked.IsBound() ? OnClicked.Execute() : FReply::Unhandled();
}

// 3) 사용 측
SNew(SSubMenuButton)
    .Label(TEXT("인벤토리"))
    .ShouldAppearHovered(false)
    .OnClicked(this, &SMyHudWidget::OpenInventory);

9. 요약

Slate 서술형 문법은 구조적 가독성, 런타임 안전성, 유연한 데이터 바인딩이라는 세 가치를 제공함. 컴포지션으로 트리를 선언하고, 스타일로 시각 통일감을 보장하며, 델리게이트와 속성 바인딩으로 입·출력을 명확히 관리하는 방법을 채택함. 레이아웃 프리미티브와 사용자 주도 레이아웃 패턴을 조합해 툴-라이크 UI를 효율적으로 구현할 수 있음