UI Toolkit是一款专为开发用户界面和编辑器扩展而制作的功能、资源和工具集合。曾作为UI Elements首次亮相的UI Toolkit为开发者们提供了一种采用保留模式(retained-mode)的UI框架,以辅助Unity扩展的定制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层级结构 ,你可以在第一时间看到什么在影响着对象,并使用选择器(selector)来保持风格的有序性。
另一个好处是关于场景层级结构 。UI Toolkit不需要一张包含几十个GameObject的Canvas,而只需要一份与UI Document相关联的UXML文件。
vs
这种工作流程对大型团队特别有利。每位团队成员都可以在自己的UXML文件上工作,然后将文件添加到场景的UI Document中,而不必几个人在同一个场景内工作,并招致可能的合并问题。
Panel Settings Asset配置文件(Assets > Create > UI Toolkit > Panel Settings Asset)确定了UXML在游戏中的实例化与显示方式。一个项目可以有多个Panel Settings Asset,让游戏的UI能应用多种不同的风格。比如,你可以为HUD应用一种面板设置,为Minimap(小地图)应用另一种设置,两者间的区别较大、使用需求也不同。
Panel Settings可被看作是UGUI Canvas与Canvas Scaler的结合体,外加文本设置等选项,让面板内的所有文本都能使用相同的基础设置。这就避免了花费大量时间手动设置每一种字体。当然,每种字体设置仍可以根据需要在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布局能自动适应容器或屏幕尺寸。你可以将其应用于包含两个及以上元素的Visual Element Tree,定义每种元素在树形结构上的对齐方式。
将一个元素放在Absolute(绝对)位置会将其放置在相对于父对象的位置,不会影响其他元素或被其他元素所影响。
对于Flex(灵活)与Align (对齐)设置,你可以参考以下简单的层级结构:
你可以使用UI Builder Inspector编辑以下设置:
请在UI Toolkit Layout Engine文档中了解更多关于视觉元素摆放的信息。
样式设计是UI Toolkit施展全部能力的地方。为视觉元素添加Style(风格)由USS文件 (Assets > Create > UI Toolkit > StyleSheet) 完成。它们相当于Unity的网页CSS文件,采用了相同的基于规则的格式。
注意,UI制作并不一定需要自定义USS文件。UI Toolkit默认提供有运行时和编辑器样式,但你可以通过自定义USS来扩展默认样式,或者从零开始创建自己的样式。
你可以在UI Builder Inspector StyleSheet 面板中添加Style选择器,然后用代码或UI Builder进行编辑。
UI Builder的USS Style选择器
在检视器内匹配脚本类
在上方截图中,.RedButton选择器被添加到一个按钮上。选择器将于所有按钮自动包含的内置.unity-text-element与.unity-button class一起出现。
下方例子中的USS规则将所有包含.RedButton类的元素的背景颜色设置为了红色。规则的第一行是一个按类名进行选择的选择器,后接几种需要应用的样式。
.RedButton { background-color: red; }
类似CSS,脚本类可以组合起来限制规则选择器的范围:
.RedButton.BlueText { color: blue; }
在例子中,只有同时具备.RedButton和.BlueText 类的对象才会有蓝色的文本。当其与前边的代码片段相结合,则按钮的背景也会变为红色。
像CSS样式一样,USS文件可以根据对象的状态来改变外观:
.unity-button:hover { background-color: red; }
在例子中,按钮在有鼠标悬停时其背景会变为红色。你可以通过点击UI Builder 工具栏的Preview按钮来预览鼠标悬停效果。
在了解了UI Toolkit的基本功能后,我们再通过一个例子来比较下UGUI与UI Toolkit的不同。我们的目标是创建一个简单的菜单,它在鼠标悬停时会产生以下两种效果:
在使用UGUI创建时,我们需要设置如下层级结构:
我们还得为部分对象添加额外的组件:
在本例中,Quit是一个红色按钮预制件。
现在,要想用UI Builder来创建菜单,我们首先要创建一个类似的层级结构:
将Popup Align设置为Center,再将Buttons Flex Direction设置为Row,此时UI Toolkit弹窗看起来会像这样:
可以看到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了解到最新的更新。或直接向产品团队进行反馈——我们期待着您的意见!
本文由Accelerate Solutions团队的Marina Joffrineau撰写,该团队由Unity最资深的开发者们组成,专为各种规模的游戏工作室提供咨询和定制开发方案服务。要想进一步了解Unity Accelerate Solutions以及我们如何帮助用户达成目标,请访问我们的网站。