Slate 에디터 UI
UE5 에디터의 모든 UI를 구성하는 Slate 프레임워크 — 선언적 문법으로 강력한 커스텀 위젯을 구축합니다
Slate 프레임워크 개요
UMG의 기반이 되는 저수준 UI 시스템
Slate는 언리얼 엔진의 커스텀 UI 프레임워크입니다. UMG(Unreal Motion Graphics)가 Slate 위에 구축된 것이며, 에디터의 모든 UI(디테일 패널, 콘텐츠 브라우저, 뷰포트 등)가 Slate로 만들어져 있습니다.
| 특성 | Slate | UMG |
|---|---|---|
| 작성 방식 | C++ 선언적 문법 | 블루프린트 + 비주얼 디자이너 |
| 대상 | 에디터 UI, 도구 | 인게임 HUD, 메뉴 |
| 성능 | 높음 (직접 렌더링) | 약간의 오버헤드 |
| UObject 의존 | 없음 (SWidget 기반) | UWidget 기반 |
| 디자이너 지원 | 없음 | UMG Designer |
에디터 메뉴에서 Tools > Debug > Widget Reflector를 열면 현재 에디터 UI의 Slate 위젯 계층 구조를 실시간으로 확인할 수 있습니다. 에디터 확장 개발의 필수 도구입니다.
SNew와 SAssignNew — 선언적 위젯 생성
Slate의 핵심 매크로와 선언적 문법 마스터하기
Slate는 선언적 문법(Declarative Syntax)을 사용합니다. SNew와 SAssignNew 매크로로 위젯 트리를 구축합니다.
SNew vs SAssignNew
| 매크로 | 반환 타입 | 용도 |
|---|---|---|
SNew(WidgetType) |
TSharedRef<SWidget> | 위젯 생성 (참조 저장 불필요) |
SAssignNew(Var, WidgetType) |
TSharedRef<SWidget> | 위젯 생성 + 멤버 변수에 할당 |
기본 위젯 생성 예제
// 간단한 텍스트 블록
SNew(STextBlock)
.Text(FText::FromString(TEXT("Hello, Slate!")))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 16))
.ColorAndOpacity(FSlateColor(FLinearColor::White));
// 버튼 위젯
SNew(SButton)
.OnClicked(this, &SMyWidget::OnButtonClicked)
.Content()
[
SNew(STextBlock)
.Text(LOCTEXT("ClickMe", "Click Me"))
];
// SAssignNew로 나중에 참조할 위젯 저장
TSharedPtr<SEditableTextBox> MyTextBox;
SAssignNew(MyTextBox, SEditableTextBox)
.HintText(LOCTEXT("Hint", "Enter text..."));
위젯 계층 구조 (슬롯)
// 수직 박스 레이아웃
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(5.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("Title", "Panel Title"))
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.Padding(5.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(STextBlock)
.Text(LOCTEXT("Content", "Content Area"))
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(5.0f)
[
SNew(SButton)
.Text(LOCTEXT("OK", "OK"))
];
AutoHeight()는 자식 위젯의 크기에 맞추고, FillHeight(N)은 남은 공간을 비율로 분배합니다. SHorizontalBox에서는 AutoWidth()와 FillWidth(N)을 사용합니다.
핵심 Slate 위젯
에디터 도구 개발에 자주 사용하는 위젯들
| 위젯 | 기능 | 주요 어트리뷰트 |
|---|---|---|
STextBlock |
텍스트 표시 | .Text(), .Font(), .ColorAndOpacity() |
SEditableTextBox |
텍스트 입력 | .Text(), .OnTextChanged(), .HintText() |
SButton |
클릭 가능 버튼 | .OnClicked(), .Text(), .IsEnabled() |
SCheckBox |
체크박스 | .IsChecked(), .OnCheckStateChanged() |
SVerticalBox |
수직 레이아웃 | Slot().AutoHeight(), .FillHeight() |
SHorizontalBox |
수평 레이아웃 | Slot().AutoWidth(), .FillWidth() |
SScrollBox |
스크롤 영역 | .Orientation(), 자식 위젯 |
SSplitter |
분할 패널 | .Orientation(), Slot() |
SBorder |
테두리/배경 | .BorderImage(), .Padding() |
SImage |
이미지 표시 | .Image(), .ColorAndOpacity() |
SListView |
리스트 뷰 | .ListItemsSource(), .OnGenerateRow() |
SComboBox |
드롭다운 | .OptionsSource(), .OnSelectionChanged() |
어트리뷰트 바인딩 패턴
// 1. 정적 값
.Text(LOCTEXT("Static", "Hello"))
// 2. 델리게이트 바인딩 (매 프레임 호출)
.Text(this, &SMyWidget::GetStatusText)
// 3. TAttribute 바인딩
.Visibility(TAttribute<EVisibility>::Create(
TAttribute<EVisibility>::FGetter::CreateLambda([this]()
{
return bShowWidget ? EVisibility::Visible : EVisibility::Collapsed;
})))
// 4. 람다 이벤트 핸들러
.OnClicked_Lambda([]()
{
UE_LOG(LogTemp, Log, TEXT("Button Clicked!"));
return FReply::Handled();
})
커스텀 Slate 위젯 만들기
SCompoundWidget을 상속하여 재사용 가능한 위젯 작성
커스텀 위젯은 SCompoundWidget을 상속하여 만듭니다. SLATE_BEGIN_ARGS / SLATE_END_ARGS 매크로로 위젯의 인자를 선언합니다.
#pragma once
#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
class SMyToolPanel : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SMyToolPanel)
: _Title(LOCTEXT("DefaultTitle", "Tool Panel"))
, _bShowHeader(true)
{}
// 어트리뷰트: TAttribute로 동적 바인딩 가능
SLATE_ATTRIBUTE(FText, Title)
// 인자: 생성 시 한 번만 설정
SLATE_ARGUMENT(bool, bShowHeader)
// 이벤트: 델리게이트 바인딩
SLATE_EVENT(FOnClicked, OnApplyClicked)
// 기본 슬롯 (자식 위젯)
SLATE_DEFAULT_SLOT(FArguments, Content)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
private:
FReply HandleApplyClicked();
FOnClicked OnApplyClicked;
};
#include "SMyToolPanel.h"
void SMyToolPanel::Construct(const FArguments& InArgs)
{
OnApplyClicked = InArgs._OnApplyClicked;
ChildSlot
[
SNew(SVerticalBox)
// 헤더
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(8.0f)
[
SNew(STextBlock)
.Text(InArgs._Title)
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 18))
.Visibility(InArgs._bShowHeader
? EVisibility::Visible
: EVisibility::Collapsed)
]
// 콘텐츠 영역
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.Padding(8.0f)
[
InArgs._Content.Widget
]
// 하단 버튼
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(8.0f)
[
SNew(SButton)
.Text(LOCTEXT("Apply", "Apply"))
.OnClicked(this, &SMyToolPanel::HandleApplyClicked)
]
];
}
FReply SMyToolPanel::HandleApplyClicked()
{
if (OnApplyClicked.IsBound())
{
return OnApplyClicked.Execute();
}
return FReply::Handled();
}
커스텀 위젯 사용
SNew(SMyToolPanel)
.Title(LOCTEXT("MyTool", "My Custom Tool"))
.bShowHeader(true)
.OnApplyClicked_Lambda([]()
{
// Apply 로직
return FReply::Handled();
})
[
// 콘텐츠 슬롯에 자식 위젯 배치
SNew(STextBlock)
.Text(LOCTEXT("Body", "Tool content goes here"))
];
| 매크로 | 용도 | 바인딩 |
|---|---|---|
SLATE_ATTRIBUTE |
동적으로 변할 수 있는 값 | TAttribute (매 프레임 평가) |
SLATE_ARGUMENT |
생성 시 한 번 설정되는 값 | 직접 값 전달 |
SLATE_EVENT |
콜백 델리게이트 | FOnClicked 등 |
SLATE_DEFAULT_SLOT |
단일 자식 위젯 슬롯 | [] 연산자로 배치 |
SLATE_NAMED_SLOT |
이름이 있는 위젯 슬롯 | .SlotName() 으로 배치 |
에디터 탭 등록하기
FGlobalTabmanager로 독립 에디터 탭 생성
커스텀 Slate 위젯을 에디터 탭으로 등록하면 에디터에서 도킹 가능한 독립 패널이 됩니다.
static const FName MyToolTabName("MyToolTab");
void FMyGameEditorModule::StartupModule()
{
// 탭 스포너 등록
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
MyToolTabName,
FOnSpawnTab::CreateRaw(
this, &FMyGameEditorModule::SpawnMyToolTab))
.SetDisplayName(LOCTEXT("TabTitle", "My Tool"))
.SetMenuType(ETabSpawnerMenuType::Hidden);
}
void FMyGameEditorModule::ShutdownModule()
{
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(MyToolTabName);
}
TSharedRef<SDockTab> FMyGameEditorModule::SpawnMyToolTab(
const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.TabRole(NomadTab)
[
SNew(SMyToolPanel)
.Title(LOCTEXT("Title", "My Editor Tool"))
];
}
// 탭 열기 (메뉴나 버튼에서 호출)
FGlobalTabmanager::Get()->TryInvokeTab(MyToolTabName);
NomadTab은 에디터 어디에나 도킹할 수 있는 독립 탭입니다. DocumentTab은 에셋 에디터처럼 중앙 영역에 배치되는 문서 탭입니다. 도구 패널에는 NomadTab을 사용하세요.
핵심 요약
- Slate는 UE5 에디터 UI의 기반 프레임워크이며, C++ 선언적 문법으로 위젯 트리를 구축합니다
- SNew로 위젯을 생성하고, SAssignNew로 참조를 보존합니다.
+Slot()으로 자식 위젯을 배치합니다 - SCompoundWidget을 상속하여 커스텀 위젯을 만들고,
SLATE_ATTRIBUTE,SLATE_ARGUMENT,SLATE_EVENT로 인터페이스를 정의합니다 - FGlobalTabmanager로 에디터 도킹 탭을 등록하여 독립 도구 패널을 생성할 수 있습니다
- Widget Reflector로 기존 에디터 UI의 Slate 구조를 분석하여 학습하세요
도전 과제
배운 내용을 직접 실습해보세요
FGlobalTabmanager::Get()->RegisterNomadTabSpawner()로 커스텀 에디터 탭을 등록하세요. SVerticalBox, STextBlock, SButton 위젯을 사용하여 간단한 도구 패널 UI를 구성하고, 버튼 클릭 시 Output Log에 메시지를 출력하세요.
SHorizontalBox, SSplitter, SScrollBox를 사용하여 좌측 목록 + 우측 상세 뷰의 2단 레이아웃을 구성하세요. FEditorStyle과 FSlateStyleSet을 활용하여 UE5 에디터와 일관된 스타일을 적용하세요.
SWindow를 사용하여 독립 에디터 윈도우를 생성하는 프레임워크를 구현하세요. 윈도우 크기 저장/복원, 도킹 지원, 커스텀 타이틀바, 키보드 단축키 바인딩을 포함하는 완성도 높은 에디터 도구 윈도우를 만드세요.