UI Toolkit在运行时:获取数据细分

UI Toolkit是一款专为开发用户界面和编辑器扩展而制作的功能、资源和工具集合。曾作为UI Elements首次亮相的UI Toolkit为开发者们提供了一种采用记忆模式(retained-mode)的UI框架,以辅助定制UI的开发。
在最新的Unity版本里,UI Toolkit从网页技术汲取了灵感,并带来了熟悉且直观的UI创作体验。本文将盘点工具各功能的特点,并分享几条用UI Toolkit创作、替代UGUI的技巧。
你可以查看这篇手册来了解两种UI系统间的深入比较。
Unity 2021 LTS目前已经开放下载与使用。该版本蕴含了整个Unity编辑器和底层基础功能一年多以来的工作结晶,以及Unity 2021.1和Unity 2021.2 Tech Stream版的特色功能(外加六个月的质量打磨)。
要想开始工作,请在Unity Hub里下载最新版的Unity。

我们来了解下UI Toolkit究竟是怎样让工作更流畅的吧。
与艺术家合作制作UI可以是一项非常复杂的任务。当艺术家在画布上泼洒颜料、添加材质时,程序员则需要创建脚本、行为和OnClick监听程序。随后,双方在合并工作成果时,有可能会出现合并冲突,并出现不少需要快速解决的问题。
UI Toolkit支持让美术人员编辑UXML和USS文件,由C#处理所有的逻辑,防止出现合并冲突。比如,按钮可以只用C#处理,脚本会按名称查询按钮并应用逻辑,不需要程序员编辑任何UXML或USS文件。
这个流程不仅能缓解合并冲突,而且还能简化未来的风格改动。如果你需要修改项目的字体,你不必一个个地检查每个资产并编辑文本。这就避免了过于繁琐的工作带来的“看走眼”问题——而越大的游戏工作起来就越复杂。
UI Toolkit的Panel Settings包含了所有的文本设置。因此,要想改变某个UI Document下的字体,你只需要编辑那些Panel Settings即可。尽管编辑器脚本同样可以辅助UGUI创作,但UI Toolkit框架可以自动化这一流程。
Visual Element是编辑按钮、图像、文本等每一种UI Toolkit元素的基础类。你可以将它视作是UI Toolkit版的GameObject。同时,UI Builder(Window > UI Toolkit > UI Builder)是一款无需代码即可创建、编辑UI的可视化工具。这对艺术家和设计师来说都非常实用,他们可以随时查看UI的制作过程。
作为一款为熟悉网页开发的开发者而准备的工具,UI Toolkit可以分离UI的逻辑和视觉风格从而重新定义工作的组织方式、避免文件冲突,从而改善美术人员与程序员之间的协作。UI Builder负责UI元素的位置、风格和设计,而C#代码可以单独请求所需要的UXML文件再应用逻辑。
UXML文件可与HTML网页文件相媲美。它们代表了视觉元素,包含了UI的层级结构。请打开Assets > Create > UI Toolkit > UI Document来创建UXML文件。然后使用UI Builder可视化并编辑新创建的UXML。
注意,UXML文件和GameObject不同,而是视觉元素的树状结构。要想精简层级结构、在一个单一的UI文档上加载所有的UXML元素。你可以在Windows > UI Toolkit > Debugger下打开UI Toolkit Debugger来可视化UI。
要想开始工作,你可以在Windows > UI Toolkit > Samples下查看UXML Element的例子。
UI Builder还支持直接创建、可视化和测试UXML文件,不必启动游戏。你只需从UI Builder Library 面板拖入UI控件(标准或自定义)到层级结构中,并组合多个UXML文件来创建最终的UI。
假设我们的用户界面由三个元素组成:
- 血量
- 分数
- “你赢了”弹窗
“你赢了”窗口将显示血量、分数以及重新开始按钮。
每种元素会在另一个UI面板中被使用,它们包含在几个单独的UML文件里,组合而成popup.uxml——文件包括了health.uxml、score.uxml外加Restart按钮。因此,管理分数和血量的代码逻辑(score.cs和health.cs)是独立于层级结构的。这意味着health.cs会一直更新health.uxml的数值,无论UI是单独显示在屏幕上还是在另一个弹窗内。
组合UXML也可以促进编辑对象的可视化。通过打开Class List 选项 ,在UI Builder层级结构中,你可以在第一时间看到什么在影响着对象,并使用选择器来保持风格的有序性:
另一个好处是关于场景层级结构。UI Toolkit不需要一张包含几十个GameObject的Canvas,而只需要一份与UI Document相关联的UXML文件。
vs
这种工作流程对大型团队特别有利。每位团队成员都可以在自己的UXML文件上工作,然后将文件添加到场景的UI Document中,而不必几个人在同一个场景内工作,并招致可能的合并问题。
面板设置资产(资产 > 创建 > UI工具包 > 面板设置资产)定义了UXML在游戏中的实例化和可视化方式。一个项目可以有多个Panel Settings Asset,让游戏的UI能应用多种不同的风格。比如,你可以为HUD应用一种面板设置,为Minimap(小地图)应用另一种设置,两者间的区别较大、使用需求也不同。
面板设置可以被视为UGUI画布 + 画布缩放器,并具有文本设置等附加选项,以便面板内的所有文本使用相同的基础设置。这就避免了花费大量时间手动设置每一种字体。当然,每种字体设置仍可以根据需要在UI Document中被改写。
UI Builder和UXML文档并不能用于管理事件。要处理按钮点击或任何其他事件,请创建一个C#脚本并将其链接到UXML。
public class UIEventHandler : MonoBehaviour
{
[SerializeField]
private UIDocument m_UIDocument;
private Label m_Label;
private int m_ButtonClickCount = 0;
private Toggle m_Toggle;
private Button m_Button;
public void Start()
{
var rootElement = m_UIDocument.rootVisualElement;
// This searches for the VisualElement Button named “EventButton”
// rootElement.Query<> and rootElement.Q<> can both be used
m_Button = rootElement.Q<Button>("EventButton");
// Elements with no values like Buttons can register callBacks
// with clickable.clicked
m_Button.clickable.clicked += OnButtonClicked;
// This searches for the VisualElement Toggle named “ColorToggle”
m_Toggle = rootElement.Query<Toggle>("ColorToggle");
// Elements with changing values: toggle, TextField, etc...
// implement the INotifyValueChanged interface,
// meaning they use RegisterValueChangedCallback and
// UnRegisterValueChangedCallback
m_Toggle.RegisterValueChangedCallback(OnToggleValueChanged);
// Cache the reference to the Label since we will access it repeatedly.
// This avoids the relatively costly VisualElement search each time we update
// the label.
m_Label = rootElement.Q<Label>("IncrementLabel");
m_Label.text = m_ButtonClickCount.ToString();
}
private void OnDestroy()
{
m_Button.clickable.clicked -= OnClicked;
m_Toggle.UnregisterValueChangedCallback(OnToggleValueChanged);
}
private void OnButtonClicked()
{
m_ButtonClickCount++;
m_Label.text = m_ButtonClickCount.ToString();
}
private void OnToggleValueChanged(ChangeEvent<bool> evt)
{
Debug.Log("New toggle value is: " + evt.newValue);
}
}
- 换行:确定哪些元素不适合并忽略它们,否则将其放置在前一个元素的上方或下方。

- 对齐项目:使用元素的最小尺寸将其打包到选定位置。

- 对齐内容:确定主轴上各元素之间的间距。

有关视觉元素定位的更多信息,请参见UI Toolkit布局引擎文档。
样式设计是UI Toolkit施展全部能力的地方。为视觉元素添加样式是通过USS文件完成的(素材资源 > 创建 > UI工具包 > 样式表)。它们相当于Unity的网页CSS文件,采用了相同的基于规则的格式。
注意,UI制作并不一定需要自定义USS文件。UI Toolkit默认提供有运行时和编辑器样式,但你可以通过自定义USS来扩展默认样式,或者从零开始创建自己的样式。
你可以通过样式选择器在UI Builder Inspector样式表 面板中添加,然后使用代码或UI Builder进行编辑。
UI Builder的USS Style选择器
在检视器内匹配脚本类
在上方截图中,.RedButton选择器被添加到一个按钮上。选择器将与所有按钮自动包含的内置.unity-text-element和.unity-button类一起出现。
下方例子中的USS规则将所有包含.RedButton类的元素的背景颜色设置为了红色。规则的第一行是一个按类名进行选择的选择器,后接几种需要应用的样式。
.RedButton { background-color: red; }类似CSS,脚本类可以组合起来限制规则选择器的范围:
.RedButton.BlueText { color: blue; }在这个例子中,只有同时具备.RedButton和.BlueText类的对象才会有蓝色的文本。当其与前边的代码片段相结合,则按钮的背景也会变为红色。
像CSS样式一样,USS文件可以根据对象的状态来改变外观:
.unity-button:hover { background-color: red; }在例子中,按钮在有鼠标悬停时其背景会变为红色。要预览悬停样式并查看效果,请点击预览在UI Builder 工具栏中。
在了解了UI Toolkit的基本功能后,我们再通过一个例子来比较下UGUI与UI Toolkit的不同。我们的目标是创建一个简单的菜单,它在鼠标悬停时会产生以下两种效果:
- 按钮背景颜色的变化
- 改变按钮的文本颜色
要使用UGUI创建此菜单,请设置以下层级结构:
我们还得为部分对象添加额外的组件:
- 菜单有一张背景图像,并需要固定以便对齐。
- 按钮必须正确锚定,以便它们正确对齐。
- 重启和退出按钮需要:
- 一个能在鼠标悬停时动态地改变文本颜色的脚本。
- 用于修改背景颜色的按钮组件设置(在编辑器中编辑)。
在本例中,Quit是一个红色按钮预制件。
现在,要使用UI Builder创建菜单,请先创建一个类似的层级结构:
将弹出窗口 对齐设置为居中,并将按钮 弹性方向设置为行,我们当前的UI工具包弹出窗口看起来像这样:
可以看到Quit按钮是红色的,它继承自QuitButton.uxml组件,该组件相当于一个按钮预制件。
要想真正看到UI工具包的神奇之处,你可以添加一个包含多条规则的PopupStyle.uss,随后...
.background {
background-image: url('/Assets/Assets/OptionsMenu.png#OptionsMenu');
width: 500px;
height: 300px;
}
.title {
font-size: 32px;
color: rgb(255, 255, 255);
}
.unity-base-field__input {
background-color: rgba(0, 0, 0, 0);
background-image: url('/Assets/Assets/OptionsMenu9Slice.png#OptionsMenu9Slice_2');
width: 300px;
height: 75px;
font-size: 20px;
color: rgba(255, 255, 255, 0.5);
-unity-text-align: middle-center;
}
.unity-button {
color: white;
background-color: rgba(0, 0, 0, 0);
background-image: url('/Assets/Assets/StartMenu.png#StartMenu_ButtonDefault');
width: 160px;
height: 30px;
-unity-slice-bottom: 1;
}
.unity-button:hover {
color: rgb(0, 21, 255);
}
#Restart {
-unity-background-image-tint-color: rgb(112, 202, 125);
}注意,选择器有优先级。直接写进UXML的样式将覆盖USS文件中的样式。这里,元素的宽高会被写到PopupStyle.uss中,但我们也可以直接在UI Builder中进行编辑并覆盖PopupStyle.uss文件。详细的优先级规则可以在此处找到。
假设我们想要更改菜单中出现的所有颜色,但不更改游戏的其余部分:UGUI需要我们手动逐个编辑所有组件的所有颜色。但在这个例子中则不同。尽管只存在一个Quit按钮的预制件,但我们可以把所有按钮都做成预制件,随后一次性覆盖所有实例的设置。接着,在经过手动修改后,菜单中的颜色会无视所有预制件上的颜色修改。
有了UI Toolkit,我们只需在新建的NewStyle.uss里复制PopupStyle.uss标签,再用NewStyle.uss替换掉PopupStyle.uss即可。
在创建和编辑完UXML文件之后,最后一步是将其添加到实际场景中。为此,我们需要替换掉Scene Canvas 元素,并创建一个带有UI Document 元素的空GameObject。在该元素中添加popup.uxml和Panel Settings 文件,然后点击Play来测试你的UI。
如果你决定尝试UI Toolkit,我们欢迎你前来UI论坛留下你的想法,或在官方文档中了解更多的细节。
最后,你可以在最新的Unity Platform Roadmap了解到最新的更新。或直接向产品团队进行反馈——我们期待着您的意见!
本文由Marina Joffrineau撰写,属于Unity Studio Productions团队,该团队由Unity最资深的软件开发人员组成。Unity Studio Productions团队为各类游戏工作室提供咨询和定制开发解决方案。要想进一步了解Unity Studio Productions以及我们如何帮助用户达成目标,请访问我们的网站。
