
Slate 프레임워크는 두 가지 핵심 요소로 구성됩니다:
Slate 위젯은 UObject에서 파생되지 않으므로 언리얼의 객체 캐스팅 시스템을 사용할 수 없고, UObject의 가비지 컬렉션도 적용되지 않습니다.
Slate는 대신 언리얼의 Shared Pointer(스마트 포인터) 프레임워크를 사용해 위젯의 메모리를 관리합니다.
공유 포인터에는 세 가지 템플릿 타입이 있습니다:
TSharedRef: null이 될 수 없고, 참조 카운트 기반의 권한 있는 스마트 포인터입니다.TSharedPtr: 참조 카운트 기반의 권한 있는 스마트 포인터입니다.TWeakPtr: 참조 카운트 기반의 약한(weak) 참조 포인터입니다.중요한 점: 모든 참조 카운트가 0이 되면 객체는 삭제되며, 이때 기본 소멸자만 호출됩니다.
(즉, 매개변수가 있는 커스텀 소멸자는 사용할 수 없습니다.)
공유 포인터를 사용하면 Slate 위젯의 소유권을 안전하게 관리할 수 있으며, 직접 delete를 호출할 필요가 없습니다.
Epic의 스마트 포인터 공식 문서도 참고하세요.
Slate 위젯을 캐스팅할 때는 표준 C++의
static_cast,const_cast를 써도 되지만, 언리얼에서 제공하는 템플릿 캐스팅 함수들을 사용하는 것이 더 편리합니다.
Slate Shared Pointer 프레임워크를 쉽게 사용할 수 있도록 도와주는 헬퍼 클래스와 함수들도 제공됩니다.
MakeShareable: C++ 포인터에서 공유 포인터를 초기화할 때 사용합니다.TSharedPtr<FMyCustomCalculator> calculator = MakeShareable(new FMyCustomCalculator());TSharedFromThis: 커스텀 C++ 클래스에서 Unreal의 Shared Pointer 프레임워크와 함께 사용하려면 상속해야 하는 클래스입니다./** 커스텀 계산을 처리하는 특수 클래스 */
class FMyCustomCalculator : public TSharedFromThis<FMyCustomCalculator>
{
public:
FMyCustomCalculator()
{ }
// ...
};StaticCastSharedRef: 스마트 포인터를 다운캐스팅할 때 사용하는 정적 캐스트 유틸리티 함수입니다. 주로 파생 타입으로 변환할 때 사용합니다.void MyFunction(FMySubCalculator& CalculatorItem)
{
TWeakPtr<FMyCustomCalculator> calculator = StaticCastSharedRef<FMyCustomCalculator>(CalculatorItem.AsShared());
// ...
}ConstCastSharedRef: const 참조를 mutable 스마트 참조로 변환합니다.void MyFunction(TSharedRef<const FMyCustomCalculator> CalculatorItem)
{
TSharedRef<FMyCustomCalculator> calculator = ConstCastSharedRef<FMyCustomCalculator>(CalculatorItem);
// ...
}StaticCastSharedPtr: 스마트 포인터를 다운캐스팅할 때 사용하는 동적 캐스트 유틸리티 함수입니다. static_cast 대신 사용할 수 있습니다(필수는 아니지만 더 간편합니다).void MyFunction(TWeakPtr<FMyCustomCalculator> CalculatorItem)
{
TSharedPtr<FMyCustomCalculator> calculator = StaticCastSharedPtr<FMyCustomCalculator>(CalculatorItem.Pin());
// ...
}ConstCastSharedPtr: const 스마트 포인터를 mutable 스마트 포인터로 변환합니다. const_cast<> 대신 사용할 수 있습니다(필수는 아니지만 더 간편합니다).void MyFunction(TSharedPtr<const FMyCustomCalculator> CalculatorItem)
{
TWeakPtr<FMyCustomCalculator> calculator = ConstCastSharedPtr<FMyCustomCalculator>(CalculatorItem);
// ...
}자세한 내용은 다음 파일을 참고하세요:Engine/Source/Runtime/Core/Public/Templates/SharedPointerTesting.iniEngine/Source/Runtime/Core/Public/Templates/SharedPointer.h슬레이트 유닛(Slate Units): 언리얼 엔진의 UI가 픽셀 밀도에 독립적으로 동작하도록 만들어주는 단위입니다.
이 덕분에 다양한 플랫폼에서 일관된 UI를 구현할 수 있습니다.
슬레이트 유닛은 실제 물리적 크기가 화면마다 다를 수 있지만, 기본적으로 1 슬레이트 유닛 = 1 픽셀로 설정되어 있습니다.
이 기본값을 바꾸고 싶다면 DPI 스케일링 값을 조정하는 것이 권장됩니다.
DPI 스케일링(DPI Scaling): 런타임에 슬레이트 유닛과 실제 픽셀 간의 변환 비율을 조정하는 방식입니다.
예를 들어, 슬레이트 유닛이 1 유닛 = 1 픽셀이고, DPI 스케일링이 2.5로 설정되어 있다면
실제로는 1 슬레이트 유닛 = 2.5 픽셀이 됩니다.
이 DPI 스케일링 값은 프로젝트 세팅의 “Engine-User Interface” 항목에서 해상도별 커브 테이블로 조정할 수 있습니다.
Epic Games에서도 완벽하진 않지만 대부분의 경우에 잘 동작한다고 설명합니다.
슬레이트 유닛에 대한 Epic의 AnswerHub 설명
Slate Users는 로컬에서 입력을 제공하는 사용자를 나타내는 클래스입니다.
예를 들어, 3인 분할 화면 협동 플레이에서는 3명의 Slate User가 생성되지만, 온라인 게임에서 32명의 플레이어가 모두 원격에 있고 이 장치에는 1명만 있을 경우 Slate User도 1명만 존재합니다.
각 플랫폼 SDK는 새로운 입력 장치(예: 컨트롤러)가 연결될 때마다 Slate Application에 새로운 Slate User를 등록(생성)하도록 알립니다.
연결이 추가되면 Slate User가 생성되지만, 연결이 제거될 때는 실수로 컨트롤러가 분리되는 상황을 고려해 Slate User 인스턴스는 삭제되지 않고 업데이트만 중단됩니다(재연결 시 기존 설정을 유지하기 위함).
Slate User 인스턴스는 현재 사용자가 포커싱 중인 위젯을 추적하며, 커서/포인터 정보(제스처 등)도 관리합니다(여러 마우스를 동시에 연결하는 것은 일반적이지 않으므로, 커서 정보는 첫 번째 Slate User만 가집니다).
Slate User에 접근하는 주요 방법은 두 가지입니다:
ULocalPlayer 객체를 통해 접근참고: Local Player는 Player Controller가 생성되기 전에 존재할 수 있으며 (Player Controller에서 Local Player가 설정될 때 호출되는 함수를 오버라이드할 수 있습니다), Local Player는 Actor가 아닌 UObject이기 때문에 레벨 간에도 유지됩니다.
// 실제 상황에 맞게 local player를 얻는 방법은 다를 수 있습니다.
// 아래 코드는 예시일 뿐, 최종/릴리즈 코드로 사용하지 마세요.
ULocalPlayer* localPlayer = nullptr;
if(UWorld* const world = GetWorld())
{
if(APlayerController* const pc = world->GetFirstPlayerController())
{
localPlayer = pc->GetLocalPlayer();
}
}
// local player가 유효한지 확인
if(!IsValid(localPlayer))
{
// 유효하지 않으면 함수 종료
return;
}
// Slate가 초기화되어 있는지 확인
if(FSlateApplication::IsInitialized())
{
// 이 함수로 local player가 연결된 Slate User를 얻을 수 있습니다.
FSlateApplication::Get().GetUser(localPlayer->GetControllerId());
}
Player Controller의 Local Player 객체를 사용해 Slate User를 얻는 예시 코드
Slate 위젯의 레이아웃 계산은 두 번의 패스(실행 순서대로)로 이루어집니다:
Horizontal Box에 TextBlock과 Image 위젯이 있을 때의 Desired Size 예시
예시에서는 Horizontal Box가 TextBlock과 Image 위젯을 자식으로 가지고 있습니다.
TextBlock의 희망 크기는 표시되는 문자열의 길이로, Image 위젯의 희망 크기는 이미지 데이터로 결정됩니다.
이 예시에서 TextBlock이 14 슬레이트 유닛, Image 위젯이 8 슬레이트 유닛의 크기를 가진다고 가정하면,
Horizontal Box의 희망 크기는 14 + 8 = 22 슬레이트 유닛이 됩니다.
Horizontal Box에 TextBlock과 Image 위젯이 있을 때 Allotted Size로 자식이 배치된 예시
이 예시에서는 Horizontal Box가 부모 위젯으로부터 25 슬레이트 유닛의 공간을 할당받았다고 가정합니다(부모 위젯은 그림에 생략).
첫 번째 슬롯(TextBlock)은 자식의 희망 크기(14 슬레이트 유닛)를 그대로 사용하고,
두 번째 슬롯(Image 위젯)은 남은 공간(11 슬레이트 유닛)을 모두 채우도록 배치됩니다.
Slate 그리기(Drawing Slate)는 Slate가 모든 표시 중인 위젯을 순회하며 Draw Elements(드로우 요소) 목록을 생성해 렌더링 시스템에 전달하는 과정입니다. 이 목록은 매 프레임마다 새로 만들어집니다.
이 과정은 On Paint 함수에서 이루어지며, 두 가지 주요 작업을 수행합니다:
Slate 위젯(UMG 위젯 포함) 자체는 별도의 틱(Tick) 함수나 틱 컴포넌트를 가지지 않으며, 틱 그룹도 존재하지 않습니다.
Slate 위젯의 틱은 Paint 패스 중에 Slate Application에서 호출되는 일련의 함수 호출 과정에서 발생합니다.
즉, 위젯이 실제로 화면에 표시되어 렌더링될 때만 틱 함수가 호출됩니다.
틱 호출 순서는 다음과 같습니다:
1. FSlateApplication::Tick
2. FSlateApplication::TickAndDrawWidgets
3. SWidget::Paint
4. SWidget::OnPaint
5. SObjectWidget::Tick
6. UUserWidget::NativeTick (이 시점에서 블루프린트의 틱 이벤트도 실행됨)
위젯 계층 구조(Widget Hierarchy)는 자식 슬롯(child slot) 개념을 통해 구현됩니다.
자식 슬롯은 선택적으로 Slate 위젯에 연결할 수 있는 객체이며, 일부 위젯(예: Image 위젯(Leaf Widget))은 자식이 없도록 설계되어 있지만, 버튼(Compound Widget)처럼 1개의 자식만 허용하거나, Overlay 위젯처럼 여러 자식을 가질 수 있도록 각 위젯별로 맞춤형 슬롯 구조를 갖습니다.
Slate 위젯은 보통 3가지 주요 유형으로 나뉩니다:
Leaf Widget: 자식 슬롯이 없는 위젯

Panel Widget: 동적으로 여러 자식 슬롯을 가질 수 있는 위젯

Compound Widget: 고정된 개수의 명시적 자식 슬롯을 가지는 위젯

모든 Slate 위젯(SWidget, C++에서 S로 시작)은 다음과 같은 주요 요소(함수 및 값)를 가집니다:
Slate & UMG의 Invalidation에 대한 Epic 공식 문서
위젯의 Desired Size(희망 크기)를 매 프레임마다 계산하는 것은 많은 위젯이 동시에 있을 때 CPU에 큰 부담을 줄 수 있습니다.
이를 방지하기 위해 위젯은 자신의 Desired Size를 캐싱(저장)하는 개념을 사용합니다.
하지만 런타임 중에 위젯의 크기가 애니메이션이나 게임 코드로 인해 변경되면,
해당 위젯은 Invalidate(무효화) 과정을 통해 Slate에 "이 위젯의 Desired Size를 다시 계산해야 한다"고 알립니다.
이후 Slate는 해당 위젯의 Desired Size를 다시 계산하고, 위젯이 속한 레이아웃을 재배치합니다.
이 방식은 매 프레임마다 모든 위젯의 Desired Size를 폴링(계속 확인)하지 않아도 되도록 최적화된 구조입니다.
위젯을 무효화할 때 지정할 수 있는 다양한 Invalidation Reason(무효화 사유)이 있습니다.
이 값들은 InvalidateWidgetReason.h에 정의되어 있습니다:
Slate(그리고 Slate를 통한 UMG)는 위젯의 속성에 속성(Attribute) 또는 함수/람다를 바인딩할 수 있는 Attribute 시스템을 지원합니다.
위젯 Attribute는 해당 위젯이 화면에 보이고(Visible) 있을 때만 업데이트되며, Collapsed 상태일 때는 업데이트되지 않습니다.
Attribute는 프로젝트 전반의 위젯 스타일을 일관되게 지정할 때 특히 유용합니다.
이를 통해 엔지니어링과 아트팀 모두가 생산성을 높일 수 있습니다.
TAttribute: 언리얼 엔진의 기본 Attribute 타입입니다. SWidget의 멤버 변수로는 사용하지 않는 것이 좋습니다.TAttribute로 상태를 변경하려면 반드시 ComputeVolatility를 오버라이드해야 합니다(TSlateAttribute/TSlateManagedAttribute는 필요 없음).TSlateAttribute: SWidget 멤버 변수로 사용하기 적합하며, Slate의 Invalidation 시스템과 연동되어 성능이 더 좋고 안전하게 Attribute를 사용할 수 있습니다.TAttribute에서 상속받지 않고, FSlateAttributeBase/TSlateMemberAttribute에서 상속받습니다.TSlateManagedAttribute 사용을 권장합니다.TSlateManagedAttribute: 배열이나 이동이 잦은 자료구조 내 SWidget 멤버 변수에 적합합니다.TAttribute에서 상속받지 않고, FSlateAttributeBase/TSlateMemberAttribute에서 상속받습니다.
TAttribute는 메모리 오버헤드가 크고 캐시 친화적이지 않으므로 신중하게 사용하세요.- 모든 Slate Attribute는 각 SWidget의
SlateAttributeMetaData에 저장되어 쉽게 접근할 수 있습니다.
SWidget에서 이벤트 및 Attribute 매크로를 선언할 때는 반드시 아래 두 매크로 사이에 위치해야 합니다:
SLATE_BEGIN_ARGS 또는 SLATE_USER_ARGS: SLATE_USER_ARGS는 위젯 구현을 소스 파일에만 두도록 강제하며, 헤더에는 선언만 남기고 모든 핸들러를 인라인화할 수 있습니다(보일러플레이트 코드 감소).SLATE_END_ARGS이 매크로들을 사용하면 위젯 생성 시 SNew, SAssignNew 등으로 생성자 지원이 가능합니다.
Slate의 Attribute 선언에는 반드시 아래 매크로를 사용해야 하며, 런타임에 Slate 위젯 생성 시 속성을 노출하려면 필수입니다.
SLATE_ATTRIBUTE:SLATE_ARGUMENT:SLATE_ARGUMENT_DEFAULT: SLATE_ARGUMENT와 동일하지만 기본값 지정 가능. 예시: SLATE_ARGUMENT_DEFAULT(float, WheelScrollMultiplier) = 1.0f;SLATE_STYLE_ARGUMENT: FSlateWidgetStyle을 상속받는 타입만 사용 가능하며, 위젯의 스타일 지정에 사용.아래는 이러한 매크로를 사용해 커스텀 버튼 위젯을 만드는 예시입니다.
class SMyButtonWidget : public SMyParentWidget
{
SLATE_DECLARE_WIDGET(SMyButtonWidget, SMyParentWidget)
public:
/** 이러한 인자들의 기본값을 설정합니다. 언더스코어(_)는 멤버 변수명과의 이름 충돌을 피하기 위함입니다. */
SLATE_BEGIN_ARGS( SMyButtonWidget )
: _Style(&FCoreStyle::Get().GetWidgetStyle< FButtonStyle >( "Button" ))
, _AdditionalPadding(FMargin(4.0f, 2.0f))
, _CanBounce(true)
{ }
/** 버튼 커스터마이즈 후 스케일 값 */
SLATE_ARGUMENT_DEFAULT( float, CustomizedScaling ) = 1.0f;
/** 버튼의 비주얼 스타일 */
SLATE_STYLE_ARGUMENT( FButtonStyle, Style )
/** 버튼의 추가 패딩 */
SLATE_ATTRIBUTE( FMargin, AdditionalPadding )
/** 클릭 시 버튼이 튈 수 있는지 여부 */
SLATE_ARGUMENT( bool, CanBounce )
SLATE_END_ARGS()
/**
* 위젯 생성자
*
* @param InArgs 이 위젯의 선언 데이터
*/
void Construct( const FArguments& InArgs )
{
// 언더스코어(_)가 붙은 값으로 인자 접근
bCanBounce = InArgs._CanBounce;
Style = InArgs._Style;
AdditionalPadding = InArgs._AdditionalPadding;
CustomizedScaling = InArgs._CustomizedScaling;
}
private:
/** 커스터마이즈 후 스케일 값 */
float CustomizedScaling;
/** 커스텀 버튼의 스타일 리소스 */
const FButtonStyle* Style;
/** 버튼 비주얼을 위한 추가 패딩 */
TSlateAttribute<FMargin> AdditionalPadding;
/** 클릭 시 버튼이 튈 수 있는지 여부 */
TSlateAttribute<bool> bCanBounce;
};
Slate 이벤트는 SWidget에서 위젯 생성 시 바인딩할 수 있는 델리게이트(Delegate)입니다.
C++에서 아래와 같이 매크로로 선언할 수 있습니다.
SLATE_EVENT: 위젯에 특정 멤버 변수로 이벤트 핸들러를 추가합니다.아래는 위젯이 Hovered(마우스 오버) 되었을 때 호출되는 이벤트를 선언한 예시입니다.
class SMyWidget : public SMyParentWidget
{
SLATE_DECLARE_WIDGET(SMyWidget, SMyParentWidget)
public:
SLATE_USER_ARGS( SMyWidget )
{ }
/** 이 위젯이 Hovered 되었을 때 호출 */
SLATE_EVENT( FSimpleDelegate, OnHovered )
SLATE_END_ARGS()
private:
/** Hovered 이벤트 델리게이트 */
FSimpleDelegate OnHovered; // FSimpleDelegate는 엔진에 내장되어 있습니다.
};
Slate 위젯 예제(UE4에서는 Slate Test Suite, UE5에서는 Starship Suite로도 불림)는 라디오 버튼, 반응형 그리드, 컬러 휠 등 다양한 Slate 기반 UI 예제를 모아둔 컬렉션입니다.
언리얼 에디터 내에서 테스트 스위트에 접근하려면 다음 단계를 따르세요:
1. 사용하는 엔진 버전에 따라 경로가 다릅니다.
Window > Developer Tools > Debug ToolsTools/Debug/Debug Tools
Test Suite를 선택하세요.
또한 엔진의 소스 코드 버전을 사용하는 경우, Test Suite 프로그램을 별도로 빌드하여 Unreal Editor를 실행하지 않고도 Test Suite를 독립 실행형 애플리케이션으로 실행할 수 있습니다.
이렇게 빌드하면 실행 파일이 [EnginePath]/Engine/Binaries/Win64/ 경로에 생성됩니다.
[EnginePath]/Engine/Source/Runtime/AppFramework/Public/Widgets/Testing/STestSuite.h[EnginePath]/Engine/Source/Runtime/AppFramework/Private/Widgets/Testing/STestSuite.cpp[EnginePath]/Engine/Source/Runtime/AppFramework/Public/Widgets/Testing/SStarshipSuite.h[EnginePath]/Engine/Source/Runtime/AppFramework/Private/Widgets/Testing/SStarshipSuite.cpp
UE4의 Test Suite는 UE5의 Starship Suite보다 현재 더 많은 기능을 제공합니다.
디버그 콘솔 명령어에 대해서는
Development & Debug Tools for UMG/Slate섹션의Debug Console Commands부분을 참고하세요.
Slate.GlobalScrollAmount [float value] (기본값 = 32.0): 마우스 휠을 한 번 굴릴 때마다 스크롤되는 양을 지정합니다(슬레이트 유닛 기준).