Choose your operating system:
Windows
macOS
Linux
虚幻引擎 4.17 版本发布后,全新的 Slate 裁剪系统打破了对 4.16 及更老版本的反向兼容。请参考此页面的 转换指南 部分更新项目。
虚幻引擎 4(UE4)的 Slate 裁剪系统可将图像或文本限制到画面的一个区域中。其实现方式是围绕创建的物体和面板创建一个裁剪矩形(或称裁剪 rect)。编辑器和 UMG UI 设计器工具均使用该系统。用户可根据需求利用 Slate 在编辑器中移动面板并调整其大小,裁剪系统则将文字和图像限制在裁剪 rect 中。结合 UMG 可实现更佳的渲染变形,对 UI 进行适当的裁剪。
全新内容
虚幻引擎 4.17 版本对裁剪系统进行了极大修改,比旧版本更灵活。这也创造了给更多可能,使 UMG 之类的工具系统能够添加更多渲染效果,用于项目的 UI 中。旧版本中的裁剪系统功能有限,布局空间仅为轴对齐,会导致难以渲染变形(常围绕边缘),如下例所示:
|
|
在上图中,控件已旋转,但裁剪 rect(白点)却并未旋转。这样一来,即使图像位于 Canvas 面板边界中,也无法被渲染。
以下列表说明了裁剪系统 1.0 和 2.0 版本之间的区别:
Clipping 1.0(4.16 及更旧版本) |
Clipping 2.0(4.17 及更新版本) |
---|---|
|
|
新系统支持在控件的渲染空间中进行裁剪,不需要轴对齐;使用系统时不再需要担心裁剪问题(左图),而是会直接裁剪掉最外侧的控件。新版本中,应用到内部控件的变形将在边缘被正确裁剪(右图)。
|
|
---|---|
4.16 与更旧版本中的裁剪和变形。 |
在 4.17 与更新版本中应用到边缘上的裁剪 |
以及子项的变形。 |
多数情况下您都无需担心控件裁剪状态的变化。以游戏 UI 为例,您无需调整 UMG 中的控件,而多数情况下都需要对其进行修改,如滚动面板或编辑器,在这些区域中用户无法控制文本长度,需要对其进行裁剪,调整窗口或面板某个部分的大小。
此处的几个例子说明了不同裁剪设置的效果:
-
左图 - 按键或文本上未启用裁剪。
-
中图 - 文本上已启用裁剪。
-
右图 - 按键上已启用裁剪。
请参考裁剪应用到不同对象上时的变化。因为系统裁剪到边界,所以填补之类的因素此处无需考虑。如果按键启用裁剪(右图),那么内容将在切断发生之前向边缘移动。如果文本负责处理裁剪(中图),那么文本将基于按键内容填补之类进行裁剪,缩减文本填入的空间。
除简单的轴对齐裁剪外,系统还支持相互堆叠的任意裁剪四边形。
举一个较为极端的例子,组合多个任意矩形是使用 3D 变形和投射的先决条件,因为被投射裁剪的控件需要在投射空间中执行裁剪。
从 4.16 及更旧版本转换到 4.17 及更新版本的指南
4.17 版本中对 Slate 裁剪系统进行的修改会破坏对旧版本的兼容性,因此您需要根据以下部分的说明来更新项目,避免出现转换问题,同时了解在项目中对控件进行调试的新方法。
启用控件裁剪
对于所有 UMG 控件而言,用户可以调整所选控件 Details 面板中的 Clipping 属性。
您可在此了解关于 用 UMG UI 设计器进行裁剪 的更多内容。
如需在代码中启用裁剪,需要将
EWidgetClipping
的裁剪属性设为以下其中一个状态:
-
Inherit
-
ClipToBounds
-
ClipToBoundsWithoutIntersecting
-
ClipToBoundsAlways
-
OnDemand
请参考以下代码范例:
SNew( SBorder )
.Clipping(EWidgetClipping::ClipToBounds)
[
...
]
弃用的 API
以下 API 在 4.16 之后的版本中弃用。如果您的项目曾使用这些 API,则需要考虑现在是否仍然需要它们。并非每个控件都执行裁剪,因此您也许不再需要那些用于移动裁剪区的代码。
-
FSlateDrawElement::Make___(...)
- 无需再将裁剪 rect 传入每个绘制调用,因此只需要移除函数调用即可自动使用新版本。 -
SScissorRectBox
- 这已不再需要,因为每个轴对齐的裁剪 rect 现在均是 scissor rect。使其最直接的子控件启用裁剪,将这些内容删除并替换之前进行的任务。
自定义裁剪
在一些情况下您可能需要在控件内部进行裁剪。举例而言,
SProgressBar
可能需要基于进度条的进度对任意位置的进度条绘制进行裁剪。添加裁剪需要在
OnPaint
中执行以下操作:
OutDrawElements.PushClip(FSlateClippingZone(AllottedGeometry));
//TODO 在此进行绘制,否则将调用子控件绘制。
OutDrawElements.PopClip();
FSlateClippingZone
是窗口空间中的任意裁剪区域,可使用数种方法开启,对已有代码进行直接转换。
如果还需要自定义裁剪影响 Hit Testing,则需要进行以下设置,将裁剪区推到 Hit Test Grid 上:
OutDrawElements.PushClip(FSlateClippingZone(AllottedGeometry));
Args.GetGrid().PushClip(FSlateClippingZone(GetCachedGeometry()));
//TODO 在此进行绘制,否则将调用子控件绘制。
OutDrawElements.PopClip();
Args.GetGrid().PopClip();
命中测试网格的裁剪 rect 使用缓存的几何体,而并非 AllotedGeometry。在
OnPaint
中,
AllotedGeometry
处于窗口空间中,而 Hit Test Grid 处于桌面空间中,因此必须使用 Tick 中获得的几何体。
包裹裁剪状态
在一些情况下,拥有裁剪状态的控件不负责进行裁剪。例如
SScrollBox
能让用户公开修改和其他控件相似的裁剪状态,然而当
SScrollBox
构造后,它会把
bClippingProxy
设为
true
。因此 Slate 渲染此控件时将忽略其裁剪状态。
在内部,
SScrollBox
告知另一个嵌套控件其需要进行裁剪或执行被告知的裁剪状态。此外,用户修改裁剪状态时,
SScrollBox
将覆盖名为
OnClippingChanged
的
SWidget
函数,使其了解合适将新的裁剪状态镜像到嵌套私有控件上。
剔除修改
虽然裁剪在渲染空间中执行,引擎仍然使用简单的边界框执行剔除。然而该框是基于渲染变形裁剪区的边界框。此外,裁剪和剔除可能随时间出现更多细微差别。如果您有一个自定义面板,执行
MyClippingRect.IntersectionWith
识别并剔除无法被绘制的控件,则需要使用
SWidget
函数
IsChildWidgetCulled
。请参考下例:
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
{
FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];
if (!IsChildWidgetCulled(MyCullingRect, CurWidget))
{
// 绘制此控件。
}
}
将
MyClippingRect
纳入 OnPaint 调用之处都需要被重命名为
MyCullingRect
。
虽然裁剪系统有所修改,但 Slate 的剔除方法仍未改变。您需要注意,如果下例中的父控件(蓝色)在裁剪区(绿色)外被剔除,则子控件(黄色)也将被剔除。即使其拥有变形,能够在父项边界外进行整体渲染,也同样如此。裁剪属性 Clip to Bounds - Without Intersecting 启用时情况也是如此,因为标记只会被直接的父控件所勾选。
调试裁剪
Widget Reflector 在编辑器中显示所选控件的裁剪状态。
打开 Widget Reflector 的方法是:前往 File 菜单,选择 Window > Developer Tools > Widget Reflector 。
以下控制台变量可用于调试编辑器中任意控件的裁剪和剔除状态: