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

MARINA JOFFRINEAU / UNITYSenior Software Engineer
Apr 20, 2022|20 Min
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。

Unity Hub登录界面
使用流程

我们来了解下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 BuilderWindow > UI Toolkit > UI Builder)是一款无需代码即可创建、编辑UI的可视化工具。这对艺术家和设计师来说都非常实用,他们可以随时查看UI的制作过程。

作为一款为熟悉网页开发的开发者而准备的工具,UI Toolkit可以分离UI的逻辑和视觉风格从而重新定义工作的组织方式、避免文件冲突,从而改善美术人员与程序员之间的协作。UI Builder负责UI元素的位置、风格和设计,而C#代码可以单独请求所需要的UXML文件再应用逻辑。

开始使用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中,而不必几个人在同一个场景内工作,并招致可能的合并问题。

Panel Settings(面板设置)

面板设置资产资产 > 创建 > 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);
   }
}
摆放

元素定位默认使用Flexbox架构。它可以保证大多数UI布局能自动适应容器或屏幕尺寸。将其应用于包含两个或更多元素的视觉元素树,然后定义它们在树上的对齐方式。

将元素放置在绝对定位意味着它将相对于其父对象的位置进行放置,并且不会影响或被其他位置影响。

对于Flex对齐 设置,可以使用以下简单的层级结构作为示例:

使用UI构建器检查器编辑以下设置:

  • 基础:这指的是项目的默认大小,在任何缩小(如果父对象没有剩余空间的缩小比例)或增大(如果父对象有剩余空间的增大比例)操作发生之前。
  • 方向(行和列):用于创建类似于UGUI的垂直布局组和水平布局组的行为。
表格垂直布局
  • 换行:确定哪些元素不适合并忽略它们,否则将其放置在前一个元素的上方或下方。
表格示例换行
  • 对齐项目:使用元素的最小尺寸将其打包到选定位置。
项目对齐选项
  • 对齐内容:确定主轴上各元素之间的间距。
元素间距选项

有关视觉元素定位的更多信息,请参见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即可。

在场景中添加UI

在创建和编辑完UXML文件之后,最后一步是将其添加到实际场景中。为此,我们需要替换掉Scene Canvas 元素,并创建一个带有UI Document 元素空GameObject。在该元素中添加popup.uxmlPanel Settings 文件,然后点击Play来测试你的UI。

现在就来试用UI Toolkit吧

如果你决定尝试UI Toolkit,我们欢迎你前来UI论坛留下你的想法,或在官方文档中了解更多的细节。

最后,你可以在最新的Unity Platform Roadmap了解到最新的更新。或直接向产品团队进行反馈——我们期待着您的意见!

本文由Marina Joffrineau撰写,属于Unity Studio Productions团队,该团队由Unity最资深的软件开发人员组成。Unity Studio Productions团队为各类游戏工作室提供咨询和定制开发解决方案。要想进一步了解Unity Studio Productions以及我们如何帮助用户达成目标,请访问我们的网站