UIToolkit
- 1. Introduction
- 2. UIToolkit
- 2.1. UI system
- 2.2. UI Assets
- 2.3. UI Tools and resources
- 2.4. 实战
- 3. Github
- 4. Reference
Introduction
之前有一篇学习Unity Editor相关的知识:UnityEditor知识
UIToolkit作为Unity最新一代UI系统,这里准备单独用篇来学习记录,通过深入学习UIToolkit,为后续编写一套节点系统做准备(用于剧情系统和引导系统实现)。
Note:
- 本章节重点是UIToolkit在Editor上的学习使用,Runtime目前看来UIToolkit还不够成熟,不适合用到Runtime(2023/5/20)
UIToolkit
看官方介绍可以看出,UI Toolkit是官方推的新一代UI系统,基于Web技术概念的一套UI系统,可用于Editor和运行时通吃。
UI Toolkit由三大部分组成:
- UI system(Contains the core features and functionality required to create user interfaces.)
- UI Assets(Asset types inspired by standard web formats. Use them to structure and style UI.)(个人理解类似UI布局文件Asset)
- Tools and resources(Create and debug your interfaces, and learn how to use UI toolkit.)
UI system
UI系统由以下部分组成:
- Visual tree(Defines every user interface you build with the UI Toolkit. A visual tree is an object graph, made of lightweight nodes, that holds all the elements in a window or panel.)(定义UI界面的数据,包含了所有组成UI显示的成员节点信息)
- Controls(A library of standard UI controls such as buttons, popups, list views, and color pickers. You can use them as-is, customize them, or create your own controls.)(UI组件)
- Data binding system(Links properties to the controls that modify their values.)(关联数据属性到UI组件)
- Layout Engine(A layout system based on the CSS Flexbox model. It positions elements based on layout and styling properties.)(类似CSS的UI布局系统)
- Event System(Communicates user interactions to elements; for example, input, touch and pointer interactions, drag and drop operations, and other event types. The system includes a dispatcher, a handler, a synthesizer, and a library of event types.)(UI事件系统,负责事件的分发等)
- UI Renderer(A rendering system built directly on top of Unity’s graphics device layer.)(在Unity基础上设计的UI渲染系统)
- UI Toolkit Runtime Support(Contains the components required to create runtime UI. The UI Toolkit package is currently in preview.)
Visual tree
Visual tree由VisualElement组成,VisualElement是所有UI Toolkit的节点基类,VisualElment包含了所有UI组件,排版,UI风格,UI事件处理等数据信息。
UI元素绘制顺序树广度扩展:
UIToolkit里的坐标系分为两种:
- Relative(position: relative相对坐标系)
- Absolute(position: absolute绝对坐标系)
上面两个坐标系和Unity里的相对位置和世界位置的概念差不多。
Note:
- 坐标原点在左上角
- VisualElementExtensions的扩展方法里提供了坐标系转换相关的方法和接口
Layout Engine
UI Toolkit includes a layout engine that positions visual elements based on layout and styling properties.(UI Toolkit的排版系统是基于VisualElement的排版和Styling属性设置决定的)
UXML format
Unity Extensible Markup Language (UXML) files are text files that define the structure of the user interface. (Unity支持用UXML来描述UI界面组件数据)
可以使用UI Builder实现可视化布局编辑。
UXML通过代码动态创建的数据对象类为VisualElementAssets。
深入使用,待学习……
Note:
- You can’t generate a VisualTreeAsset from raw UXML at runtime.
Uity style sheets(USS)
Style properties are either set in C# or from a style sheet. Style properties have their own data structure (IStyle
interface). UI Toolkit supports style sheets written in USS (Unity style sheet).(Unity支持定义类似CSS的USS文件来定义UI界面使用的Style属性设置)
USS通过代码动态创建的数据对象类为StyleSheet
USS包含以下部分:
- Style rules that include a selector and a declaration block.(个人理解Style定义包含选择器和内容定义)
- Selectors that identify which visual element the style rule affects.(选择器用于匹配Element的Style使用)
- A declaration block, inside curly braces, that has one or more style declarations. Each style declaration has a property and a value. Each style declaration ends with a semi-colon.(内容定义Style的详细配置)
USS Selector
Style选择器分有很多种,简单的选择器有:
Type selector
type selectors match elements based on their element types.(通过类型名字匹配)
1 | Button { border-radius: 8px; width: 100px; } |
1 | <Button name="Cancel" text="Cancel" /> |
Note:
- type selector的名字不允许包含命名空间
Class selector
class selectors match elements that have specific USS classes assigned. (通过定义类名匹配)
1 | .yellow { background-color: yellow; } |
1 | <Button name="OK" class="yellow" text="OK" /> |
Note:
- calss selector大小写敏感且不允许带数字
Name selector
name selectors match elements based on the name of an element.(通过名字匹配)
1 | #Cancel { border-width: 2px; border-color: DarkRed; background-color: pink; } |
1 | <Button name="Cancel" text="Cancel" /> |
Note:
- 代码里可以通过VisualElement.name修改去匹配Name Selector
Universal selector
universal selector, also called the wildcard selector, matches any element.(通过通配符(正则)去匹配)
1 | * { background-color: yellow; } |
1 | <VisualElement name="container1"> <VisualElement name="container2" class="yellow"> <Button name="OK" class="yellow" text="OK" /> <Button name="Cancel" text="Cancel" /> </VisualElement> |
更多复杂的选择器参考:
USS properties
USS property assigns a style or behavior to a VisualElement. (USS的内容属性用于给VisualElement指定具体显示配置)
USS data types
Syntax - auto,
如果一个属性有多个值,可以如下方式表达不同含义:
Side-by-side表示全部都必须按顺序出现
|表示多个选择里必须出现一个
||表示1个或多个必须按顺序出现
&&表示所有都必须出现
[]表示一个选择组(类似正则里[]的概念)
上面提到的属性多个值都支持类似正则里*,+,?,{A,B}的后缀描述,表达的含义也就是类似正则里的出现次数控制。
Length - 支持像素(px)和百分比(%)两种长度单位
initial - 全局关键词,标识重置属性到初始默认值
Color - 支持16进制(#FFFFFF)和rgb表达方式(rgba(255, 255, 255, 1))
Color关键词详情查看:
Assets - 支持引用项目里的资源,resource()(标识使用Resources目录资源),url()(标识使用项目Asset资源,支持相对USS所在目录的相对路径和项目相对路径)
USS common properties
在了解一些常规布局属性概念前,让我们先来看看官网给的一张示意图:
宽高属性
width(宽度),height(高度),min-width(最小宽度),min-height(最小高度),max-width(最大宽度),max-height(最大高度)……
边缘(Margins)属性
margin-left(左侧边缘),margin-top(顶部边缘),margin-right(右侧边缘),margin-bottom(底部边缘)……
边界(Border)属性
border-left-width(左侧边界),border-top-width(顶部边界),border-right-width(右侧边界),border-bottom-width(底部边界),border-color(边界颜色)……
内边距(Padding)属性
padding-left(左侧内边距),padding-top(顶部内边距),padding-right(右侧内边距),padding-bottom(底部内边距)……
排版属性
- Items
- flex-grow(排版放大设置)
- flex-shrink(排版缩小设置)
- flex-basic(排版大小设置)
- align-self(自身对齐设置)
- ……
- Containers
- flex-direction(子成员排版对齐方向)
- flex-wrap(子成员放不下是否多行显示排版)
- align-content(子成员在排版方向上的对齐方式)
- ……
位置属性
position(位置坐标系-绝对|相对),left(距离父容器左测距离),top(距离父容器顶部距离),right(距离父容器右侧距离),bottom(距离父容器底部距离)……
UIToolkit里的坐标属性有两种坐标系:
- 绝对坐标
- 相对坐标
Note:
- 设置了Left,Top,Right,Bottom后,Width属性相关设置会被忽略。
背景属性
background-color(背景颜色),backtground-image(背景图片),-unity-background-scale-mode(背景图片缩放模式),-unity-background-image-tint-color(背景图片色调?)……
九宫属性
-unity-slice-left(左侧切割数值),-unity-slice-top(顶部切割数值),-unity-slice-right(右侧切割数值),-unity-slice-bottom(底部切割数值),-unity-slice-scale(九宫缩放值?)……
Appearance(外观)属性
overflow(溢出显示设置),-unity-overflow-clip-box(溢出裁剪box设置),-unity-paragraph-spacing(段落间隔设置),opacity(透明度显示设置),visibility(可见性设置),display(排版显示设置)……
文本属性
color(绘制颜色设置),-unity-font(绘制文本字体设置),-unity-font-definition(?),font-size(字体大小设置),-unity-font-style(字体风格设置),-unity-text-align(字体对齐设置),-unity-text-overflow-position(?),white-space(空格处理方式设置),-unity-text-outline-width(描边宽度设置),-unity-text-outline-color(描边颜色设置)……
详细USS properties查看:
W3C CSS的详细属性效果查看:
Note:
- USS properties use the same syntax as W3C CSS documents(USS的内容属性采用W3C CSS相关文档规则)
- The USS display property supports only a small subset of the CSS
display
property’s available keyword values.(USS的属性支持只是CSS的很小部分子集)
USS custom properties
可以看到USS支持在USS文件里自定义变量,然后像编程里的变量一样,一处定义多处使用。
USS自定义变量规则如下:
—变量名:值
USS自定义变量在其他USS文件访问规则:
var(—变量名, 默认值(可选))
内置自定义变量查询:
USS built-in variable references
Best practices for USS
官方关于使用USS Style的建议:
- Avoid inline styles(个人理解是使用全局USS File,避免对单个Visual Element设置Styles(会导致耗费更多内存))
- Selector architecture consideration(选择合适的selector方案,过多或过于复杂的selector方案会导致运行时选择style开销更大)
更多建议参考:
Theme Style Sheet(TSS)
更多学习,待添加……
Query Elements
Unity提供了Query功能(类似Linq),方便用户快速从VisualTree或Elements里查找符合条件的Element。
UQuery
UQuery is a set of extension methods allowing you to select individual or collection of visualElements inside a complex hierarchy.(一系列扩展方法用于查询指定VisualElements)
UQueryBuilder
Utility Object that contructs a set of selection rules to be ran on a root visual element.(工具类,辅助UQuery构建一系列查询规则)
Q
Q is the shorthand for Query
.First(). It returns the first element that matches the selection rules. (Q是查询首个符合规则的Element的缩写)
Event System
Dispatch Events
UIToolkit Event System监听系统事件,然后通过EventDispatcher派发事件给Visual Element。
所有的事件派发都会经历以下三个阶段:
- Trickles down: Events sent to elements during the trickle down phase.(个人没太理解这个阶段,感觉是一个事件捕获从上往下派发事件的过程(不含响应事件的最里层Visual Element)
- Bubbles up: Events sent to elements during the bubble-up phase.(事件从目标节点向上冒泡派发给Visual Element的,即最里层符合的Visual Element最先派发(从下往上)(含响应事件最里层Visual Element))
- Cancellable: Events that can have their default action execution cancelled, stopped, or prevented.(事件是可取消的,比如一般的事件冒泡为了不继续想外层冒泡,我们会设置事件阻断防止继续冒泡)
通过上面的介绍可以了解到,事件派发有两个流程(Trickles down & Bubbles up),在不打断事件派发的前提下,最里层响应Visual Element只会派发一次,其他外层传递路线上的Visual Element会派发两次事件。
事件基类为EventBase,所有相关事件介绍参考:
Note:
- The UI Toolkit event system shares the same terminology and event naming as HTML events(UIToolkit采用和HTML一样的事件名和术语)
Handle Events
事件处理顺序如下:
- Execute event callbacks on elements from the root element down to the parent of the event target. This is the trickle-down phase of the dispatch process.(事件捕获阶段从上往下触发事件)
- Execute event callbacks on the event target. This is the target phase of the dispatch process.(在目标响应Visual Element上触发事件)
- Call ExecuteDefaultActionAtTarget() on the event target.(在目标响应Visual Element上触发ExecuteDefaultActionAtTarget()方法)
- Execute event callbacks on elements from the event target parent up to the root. This is the bubble-up phase of the dispatch process.(从目标Visual Element开始向上冒泡触发事件)
- Call ExecuteDefaultAction() on the event target.(在目标响应Visual Element上触发ExecuteDefaultAction()方法)
事件响应里有两个重要的概念:
- Event.currentTarget is the visual element on which the callback was registered.(当前注册响应事件的Visual Element)
- Event.target is the element where the event occurs, for example the element directly under the mouse.(当前事件发生的Visual Element)
By default, a registered callback executes during the target phase and the bubble-up phase. This default behavior ensures that a parent element reacts after its child element.(默认状态下,我们注册的事件回调是在target phase和bubble-up phase阶段执行。这是为了确保事件回调触发是从下往上)
如果我们想在指定阶段(e.g. tickle-down phase或bubble-up phase)触发注册事件回调,我们需要在注册事件回调处,显示传参说明响应阶段:
1 | VisualElement myElement = new VisualElement(); |
如果我们想事件传递自定义数据,我们需要自定义一个含自带数据的方法回调并监听事件:
1 | myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData); |
相关事件信息查询:
SerializedObject Data Binding
待添加……
UI Assets
Unity提供了两种Asset用于布局系统
- UXML documents(Unity eXtensible Markup Language (UXML) is an HTML and XML inspired markup language that you use to define the structure of user interfaces and reusable UI templates.)(类似HTML和XML的标记性语言,用于定义UI布局)
- Unity Style Sheets(USS)(Style sheets allow you to apply visual styles and behaviors to user interfaces. They’re similar to Cascading Style Sheets (CSS) used on the web, and support a subset of standard CSS properties. )(类似CSS用于定义UI风格)
UI Tools and resources
Unity提供了几个辅助工具,帮助我们学习和调试UI Toolkit
UI Debugger(UI调试器—可视化查看UI节点详细数据)
UI Builder(UI布局可视化编辑器(预览版本))
UI Samples(UI事例)
实战
接下来通过实战学习,进一步了解UIToolkit的使用和设计。
基础学习
代码+USS创建UI
代码创建UI
UIToolkit为了帮助我们快速创建Editor UI窗口,提供了快捷创建入口:
Asset->Create->UI Toolkit->Editor Window
考虑到不适用UXML作为UI布局,使用纯代码写Editor UI,这里就不勾选UXML了。
创建完会看到生成一个.cs文件和一个.uss文件,这是因为我们勾选了C#和USS,接下来我们打开UICreateUIByCodeEditorWindow.cs开始我们的Editor UI代码编写,后续会讲到如何利用*.uss文件来指定UI风格。
UICreateUIByCodeEditorWindow.cs
1 | public class UICreateUIByCodeEditorWindow : EditorWindow |
可以看到编写基于UIToolkit的Editor Window还是和以往一样,要继承EditorWindow,但实现GUI绘制的接口不是OnGUI()而是CreateGUI()。
正如前面提到的UIToolkit里绘制的对象Visual Tree是由Visual Element组成的树状结构。而EditorWindow.rootVisualElement正是UIToolkit绘制EditorWindow的根VisualElement,我们所有的UIToolkit元素都是添加到此EditorWindow.rootVisualElement实现排版绘制的。
代码修改USS
所有的UIToolkit组件都继承至VisualElement,想通过代码修改UIToolkit组件的Style很简单,直接访问VisualElement.style即可。
UIChangeUSSByCodeEditorWindow.cs
1 | /// <summary> |
可以看到通过访问style属性并修改其中的color属性,我成功的修改了标题文本的颜色显示风格。更多的属性修改参考文档:
使用USS指定UI风格
第一步依然是使用Create->UI Toolkit->Editor WIndow
去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。
UIUseUSSEditorWindow.cs
1 | public class UIUseUSSEditorWindow : EditorWindow |
UIUseUSSEditorWindow.uss
1 | Label { font-size: 20px; color: rgb(68, 138, 255); } .SmallLabel { font-size: 10px; color: rgb(68, 138, 255); } .BigLabel { font-size: 40px; color: rgb(68, 138, 255); } .BoldLabel { font-size: 20px; -unity-font-style: bold; color: rgb(68, 138, 255); } #CenterButton { align-self: center; -unity-text-align: middle-center; } |
可以看到我们在UIUseUSSEditorWindow.uss里分别定义了,Type Selector(Label),Class Selector(.SmallLabel, .BigLabel, .BoldLabel),Name Selector(CenterButton)。
对应的我们在代码里通过以下三种方式分别指定了Selector:
Type Selector
1
VisualElement labelWithStyle = new Label("USS Using Label Type Selector!");
Class Selctor
1
2
3
4
5
6
7VisualElement smallLabelWithStyle = new Label("USS Using SmallLabel Class Selector!");
smallLabelWithStyle.AddToClassList("SmallLabel");
VisualElement bigLabelWithStyle = new Label("USS Using BigLabel Class Selector!");
smallLabelWithStyle.AddToClassList("BigLabel");
VisualElement labelWithStyle = new Label("USS Using Label Type Selector!");
VisualElement boldLabelWithStyle = new Label("USS Using BoldLabel Class Selector!");
boldLabelWithStyle.AddToClassList("BoldLabel");Name Selector
1
2
3Button ussCenterButton = new Button();
ussCenterButton.name = "CenterButton";
ussCenterButton.text = "Center USS Button Name Selector";
位置和排版
第一步依然是使用Create->UI Toolkit->Editor WIndow
去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。
UIPositionAndLayoutEditorWindow.cs
1 | /// <summary> |
可以看到通过不断创建需要排版的容器设置对应排版和大小属性,我们成功的创建出了各种排版方向以及相对位置和绝对位置的显示UI。
自定义USS变量
在创建USS文件时,我们很多时候会填充相同值给不同的Selector,而修改时并不想一个一个去修改,这个时候就需要用到类似编程上定义变量公用同一个变量的方式。
第一步依然是使用Create->UI Toolkit->Editor WIndow
去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。
UICustomUSSVariableEditorWindow.cs
1 |
|
UICustomUSSVariableEditorWindow1.uss
1 | :root { --text-color: red; --background-color: green; --text-color2: yellow; --background-color2: blue; } .NormalLabel { color: red; background-color: var(--background-color); align-self: center; } |
UICustomUSSVariableEditorWindow2.uss
1 | .NormalLabel2 { color: var(--text-color2); background-color: var(--background-color2); align-self: center; } |
可以看到我们在USS文件里自定义了变量并在Class Selector里使用,通过加载多个USS文件,我们实现了跨USS文件的变量定义访问使用。
UXML创建UI
待添加……
UI事件响应
数据绑定
调试
知识点
- EditorWindow.CreateGUI()方法是用于UIToolkit创建Editor窗口的rootVisualElement显示方法
- Editor.CreateInspectorGUI()方法是用于UIToolkit创建自定义Inspector面板的显示方法
- UI Toolkit to create Editor UI and synchronize data between a property and a visual element for the Editor UI.(UIToolkit支持在Editor UI上同步属性数据到Visual Element上)
节点编辑器实现
Github
个人Github: