ue Slate初探
背景
在开发上层UMG过程中,有时候会遇到需要更底层的支持,以此为契机,研究Slate使用与底层注意事项。
基本使用
Slate是UE中提供的UI框架,它的核心理念如下:
关于UI的一些基础概念,可以了解:
在UE中,使用Slate,需要了解三个核心结构:
- FSlateApplication :全局单例,所有UI的调度中心。
- SWindow :顶层窗口,持有跨平台窗口的实例(FGenericWindow),提供窗口相关的配置和操作。
- SWidget :小部件,划分窗口区域,处理自身区域内的交互和绘制事件。
在UE中,一个简单的Slate使用示例如下:
1 | auto Window = SNew(SWindow) //创建窗口 |
Slate的代码风格如下:
- Slate 控件的类命名一般以
S开头 - 可以通过以下函数来快速构建Slate控件:
SNew( WidgetType, ... ):通用的构造方式SAssignNew( ExposeAs, WidgetType, ... ):构造完成后,把控件赋值给ExposeAsSArgumentNew( InArgs, WidgetType, ... ):使用参数集进行构造
- 在构造的代码表达式中,可以通过一些函数来设置控件的构造参数,由于这些函数都会返回控件自身的引用,因此可以进行链式调用
- 可以通过
operatpr .调用函数来设置控件的属性和事件
- 可以通过
- 对于 SPanel 的子类,可以使用
operator +SPanelType::Slot添加子控件的插槽- 对于 SCompoundWidget
和SPanel::Slot ,可以使用operator []来填充子控件
- 对于 SCompoundWidget
- 对于Slate的属性和事件,可以通过多种方式绑定,比如上面的
.Text(...)设置的是静态值,还可以通过绑定函数来动态获取属性值:Text_Lambda(...)
Text_Raw(...)Text_Static(...)
Text_UObject(...)
Slate可以同时作为 Game UI 和 Editor UI
在Editor中,可以通过如下方式来添加UI:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23auto Window = SNew(SWindow) //必须具有一个顶层窗口
.Title(FText::FromString("CustomWindow")) //设置窗口标题
.ClientSize(FVector2D(600, 600)) //设置窗口大小
[ //填充窗口内容
SNew(SSpacer) //自身控件,这里是一个空白填充
];
//方法1:注册该窗口,并立即显示
FSlateApplication::Get().AddWindow(Window, true);
//方法2:注册该窗口,不显示,手动调用ShowWindow来显示
FSlateApplication::Get().AddWindow(Window, false); //注册该窗口,并立即显示
Window->ShowWindow();
//----------通过DockTab的方式来添加窗口: -------------
// 注册Tab页面的生成器
FGlobalTabmanager::Get()->RegisterTabSpawner(FName("CustomTab"),FOnSpawnTab::CreateLambda([](const FSpawnTabArgs& Args){
return SNew(SDockTab)
[
SNew(SSpacer)
];
}));
FGlobalTabmanager::Get()->TryInvokeTab(FTabId("CustomTab")); //尝试激活Tab页面
在Game中,可以通过以下方式来添加UI:
Using Slate In-Game in Unreal Engine | Unreal Engine 5.2 Documentation | Epic Developer Community
1 |
|
Swidget SWindow
- Swidget 结构
1 | TAttribute<bool> EnabledState, //开启状态,指定是否能够与控件交互,如果禁用,则显示为灰色 |
- Swindow结构
1 | EWindowType Type; //窗口类型 |
Swidget基本开发
基本类

Slate开发的绝大部分工作内容可以归纳为:
- 设置控件属性
- 绑定事件逻辑
- 组织层次结构
SWidget 是一个抽象基类,UE根据不同的使用方式,又对其进行派生,划分为四大类:
SCompoundWidget :可以设置ChildSlot (SBoder/SButton/…)
Slate 用来提供给开发者的主要扩展方式,一般继承它是为了利用已有的SWidget来组织一系列的Widget。
SPanel :可以看做是SWidget的容器,可以包含一个或多个ChildSlot,用于添加SWidget。 (Soverlay/SBoxPanel.SHorizontalBox/SBoxPanel.SVerticalBox/…)
Slate已经派生了足够多的SPanel,用于组织Slate的布局等各类操作,一般情况下很少对其派生。
SLeafWidget :叶子控件,不包含ChildSlot (SImage/STest/…)
派生它,主要是为了自定义一些拥有独特 渲染和尺寸处理机制 的控件
SWeakWidget :定义逻辑上的归属而非事件上的。
SPanel中的SWidget在事件上具有层级关系,但有时会出现一些特殊情况,比如点击按钮打开一个菜单,菜单可以看做是隶属于这个按钮的,但本质上菜单是新开启了一个窗口,它们在事件传递上并不存在层级关系,所以就得靠SWeakWidget来解决这个问题。一般情况下很少使用它。
大多时候,我们会新增一个C++类,继承自 SCompoundWidget ,在Construct函数中填充子控件,就像是这样:
1 | class SCustomWidget: public SCompoundWidget |
这样我们就可以使用如下代码来创建该控件:
1 | auto MyWidget = SNew(SCustomWidget); |
如果想增加 参数(Argument) ,比如说让文本内容可以设置,那么可以把定义改为:
1 | class SCustomWidget : public SCompoundWidget |
这样就可以使用如下代码设置控件内容:
1 | auto MyWidget = SNew(SCustomWidget) |
当然,我们也可以不走FArguments的方式,直接这样:
1 | class SCustomWidget : public SCompoundWidget |
如果想让上述参数能够绑定委托,就需要使用Slate的 属性(Attribute) 机制:
1 | class SCustomWidget : public SCompoundWidget |
然后就能使用这样的代码:
1 | auto MyWidget = SNew(SCustomWidget) |
如果想增加一些控件的事件处理回调,比如说当上面文本变动时的做一些处理,那么可以用 Slate 的 事件(Event) 机制:
1 | class SCustomWidget : public SCompoundWidget |
此外,SLATE_BEGIN_ARGS(...)和SLATE_END_ARGS()之间还有其他可供使用的宏,使用频率不高,因此这里不展开描述,具体可以查看:
Engine\Source\Runtime\SlateCore\Public\Widgets\DeclarativeSyntaxSupport.h
综上,Slate的开发流程基本如此。
控件一览
对于UI开发,需要了解以下的基础概念(UMG UGUI 都有此结构):
- 窗口的基本状态 :激活(Active),焦点(Focus),可见(Visible),模态(Modal),变换(Transform)
- 布局策略及相关概念
- 盒式布局(HBox,VBox),流式布局(Flow),网格布局(Grid),锚式布局(Anchors),重叠布局(Overlap),画布(Canvas)
- 内边距(Padding),外边距(Margin),间距(Spacing),对齐方式(Alignment)
- 样式管理 :图标(Icon),风格(Style),画刷(Brush)
- 字体管理 :字体类型(Font Family),文本宽度测量(Font Measure)
- 尺寸计算 :控件尺寸计算策略
- 交互事件 :鼠标,键盘,拖拽,焦点事件,事件处理机制,鼠标捕获
- 绘制事件 :绘制元素,区域裁剪
- 基本控件 :标签(Label),按钮(Button),复选框(Check),组合框(Combo),滑动条(Slider),滚动条(Scroll Bar),文本框(Text),对话框(Dialog),颜色选取(Color),菜单栏(Menu Bar),菜单(Menu),状态栏(Status Bar),滚动面板(Scroll ),堆栈(切换)面板(Stack/Switcher),列表面板(List),树形面板(Tree)…
- 国际化 :文本本地化翻译(Localization)
在1 - Slate 开发 - Modern Graphics Engine Guide其中控件一览描述,此篇省略。
UMG2Slate
UMG怎么包装Slate?
每一个UWidget里面都对应一个Slate控件。
当你在蓝图中添加一个和Button,UE会:
1.创建一个UButton(UObject)
2.在底层生成对应的SButton(Slate)
3.通过TakeWidget()将Slate控件挂载到Viewport.
如何访问 UMG 控件的底层 Slate?
(1)在C++中获取Slate控件
1 | UButton* MyUMGButton = ...; // 获取 UMG 按钮 |
(2)在蓝图中控制
UMG暴露了部分Slate的属性,但是无法直接访问Slate对象;
为什么要有 UMG?
Slate 虽然灵活,但存在以下问题:
- 不适合游戏开发:需要手动管理内存(
TSharedPtr),没有蓝图支持。 - 跨平台困难:移动端输入处理复杂。
- 迭代效率低:修改 UI 需重新编译 C++。
UMG 通过以下方式优化:
- 蓝图驱动:设计师可独立开发 UI。
- 自动垃圾回收:基于
UObject系统。 - 跨平台抽象:统一处理触摸/手柄输入。
UMG嵌入到Slate
重点是将UMG嵌入到Slate里面,容易出现内存泄漏的问题。
UMG是继承自UObject的UE自动垃圾回收的机制,但是则Slate是使用智能指针去管理内存,要分析一些智能指针是否会影响UE的垃圾回收,从而导致出现了垃圾回收不掉的内存泄漏问题。
参考
1 - Slate 开发 - Modern Graphics Engine Guide
【UE·底层篇】Slate源码分析——点击事件的触发流程梳理 - 知乎
[UMG和Slate 源码分析和混合使用 | LTQ的BLOG](https://liutianqi0123.github.io/2025/08/12/UE UMG和Slate 源码分析和混合使用/)

