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。
以下控制台变量可用于调试编辑器中任意控件的裁剪和剔除状态: