文章目錄
  1. 1. Introduction
  2. 2. UIToolkit
    1. 2.1. UI system
      1. 2.1.1. Visual tree
      2. 2.1.2. Layout Engine
      3. 2.1.3. UXML format
      4. 2.1.4. Uity style sheets(USS)
        1. 2.1.4.1. USS Selector
          1. 2.1.4.1.1. Type selector
          2. 2.1.4.1.2. Class selector
          3. 2.1.4.1.3. Name selector
          4. 2.1.4.1.4. Universal selector
        2. 2.1.4.2. USS properties
          1. 2.1.4.2.1. USS data types
          2. 2.1.4.2.2. USS common properties
            1. 2.1.4.2.2.1. 宽高属性
            2. 2.1.4.2.2.2. 边缘(Margins)属性
            3. 2.1.4.2.2.3. 边界(Border)属性
            4. 2.1.4.2.2.4. 内边距(Padding)属性
            5. 2.1.4.2.2.5. 排版属性
            6. 2.1.4.2.2.6. 位置属性
            7. 2.1.4.2.2.7. 背景属性
            8. 2.1.4.2.2.8. 九宫属性
            9. 2.1.4.2.2.9. Appearance(外观)属性
            10. 2.1.4.2.2.10. 文本属性
        3. 2.1.4.3. USS custom properties
        4. 2.1.4.4. Best practices for USS
        5. 2.1.4.5. Theme Style Sheet(TSS)
      5. 2.1.5. Query Elements
      6. 2.1.6. Event System
        1. 2.1.6.1. Dispatch Events
        2. 2.1.6.2. Handle Events
      7. 2.1.7. SerializedObject Data Binding
    2. 2.2. UI Assets
    3. 2.3. UI Tools and resources
    4. 2.4. 实战
      1. 2.4.1. 基础学习
        1. 2.4.1.1. 代码+USS创建UI
          1. 2.4.1.1.1. 代码创建UI
          2. 2.4.1.1.2. 代码修改USS
          3. 2.4.1.1.3. 使用USS指定UI风格
          4. 2.4.1.1.4. 位置和排版
          5. 2.4.1.1.5. 自定义USS变量
        2. 2.4.1.2. UXML创建UI
        3. 2.4.1.3. UI事件响应
        4. 2.4.1.4. 数据绑定
        5. 2.4.1.5. 调试
        6. 2.4.1.6. 知识点
      2. 2.4.2. 节点编辑器实现
  3. 3. Github
  4. 4. Reference

Introduction

之前有一篇学习Unity Editor相关的知识:UnityEditor知识

UIToolkit作为Unity最新一代UI系统,这里准备单独用篇来学习记录,通过深入学习UIToolkit,为后续编写一套节点系统做准备(用于剧情系统和引导系统实现)。

Note:

  1. 本章节重点是UIToolkit在Editor上的学习使用,Runtime目前看来UIToolkit还不够成熟,不适合用到Runtime(2023/5/20)

UIToolkit

UI Toolkit is the newest UI system in Unity. It’s designed to optimize performance across platforms, and is based on standard web technologies. You can use UI Toolkit to create extensions for the Unity Editor, and to create runtime UI for games and applications (when you install the UI Toolkit package.

看官方介绍可以看出,UI Toolkit是官方推的新一代UI系统,基于Web技术概念的一套UI系统,可用于Editor和运行时通吃。

UI Toolkit由三大部分组成:

  1. UI system(Contains the core features and functionality required to create user interfaces.)
  2. UI Assets(Asset types inspired by standard web formats. Use them to structure and style UI.)(个人理解类似UI布局文件Asset)
  3. Tools and resources(Create and debug your interfaces, and learn how to use UI toolkit.)

UI system

UI系统由以下部分组成:

  1. 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显示的成员节点信息)
  2. 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组件)
  3. Data binding system(Links properties to the controls that modify their values.)(关联数据属性到UI组件)
  4. Layout Engine(A layout system based on the CSS Flexbox model. It positions elements based on layout and styling properties.)(类似CSS的UI布局系统)
  5. 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事件系统,负责事件的分发等)
  6. UI Renderer(A rendering system built directly on top of Unity’s graphics device layer.)(在Unity基础上设计的UI渲染系统)
  7. 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元素绘制顺序树广度扩展:

UIToolkitDrawingOrder

UIToolkit里的坐标系分为两种:

  1. Relative(position: relative相对坐标系)
  2. Absolute(position: absolute绝对坐标系)

上面两个坐标系和Unity里的相对位置和世界位置的概念差不多。

Note:

  1. 坐标原点在左上角
  2. 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。

UXML现有可用Element查询

UXML布局构建事例

深入使用,待学习……

Note:

  1. 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包含以下部分:

  1. Style rules that include a selector and a declaration block.(个人理解Style定义包含选择器和内容定义)
  2. Selectors that identify which visual element the style rule affects.(选择器用于匹配Element的Style使用)
  3. 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>

更多复杂的选择器参考:

Complex selectors

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关键词详情查看:

USS color keywords

Assets - 支持引用项目里的资源,resource()(标识使用Resources目录资源),url()(标识使用项目Asset资源,支持相对USS所在目录的相对路径和项目相对路径)

USS common properties

在了解一些常规布局属性概念前,让我们先来看看官网给的一张示意图:

USSBoxModel

宽高属性

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里的坐标属性有两种坐标系:

  1. 绝对坐标
  2. 相对坐标

Note:

  1. 设置了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查看:

USS properties reference

W3C CSS的详细属性效果查看:

CSS

Note:

  1. USS properties use the same syntax as W3C CSS documents(USS的内容属性采用W3C CSS相关文档规则)
  2. The USS display property supports only a small subset of the CSS display property’s available keyword values.(USS的属性支持只是CSS的很小部分子集)

USS custom properties

USS variables, also called custom properties, define values that you can reuse in other USS rules. You can create variables for any type of USS property.

可以看到USS支持在USS文件里自定义变量,然后像编程里的变量一样,一处定义多处使用。

USS自定义变量规则如下:

—变量名:值

USS自定义变量在其他USS文件访问规则:

var(—变量名, 默认值(可选))

内置自定义变量查询:

USS built-in variable references

Best practices for USS

官方关于使用USS Style的建议:

  1. Avoid inline styles(个人理解是使用全局USS File,避免对单个Visual Element设置Styles(会导致耗费更多内存))
  2. Selector architecture consideration(选择合适的selector方案,过多或过于复杂的selector方案会导致运行时选择style开销更大)

更多建议参考:

Best practices for USS

Theme Style Sheet(TSS)

Theme Style Sheet (TSS) files are regular USS files. UI Toolkit treats TSS as a distinct asset type and uses it for management purposes.

更多学习,待添加……

Query Elements

Unity提供了Query功能(类似Linq),方便用户快速从VisualTree或Elements里查找符合条件的Element。

Event System

Dispatch Events

UIToolkit Event System监听系统事件,然后通过EventDispatcher派发事件给Visual Element。

所有的事件派发都会经历以下三个阶段:

  1. Trickles down: Events sent to elements during the trickle down phase.(个人没太理解这个阶段,感觉是一个事件捕获从上往下派发事件的过程(不含响应事件的最里层Visual Element)
  2. Bubbles up: Events sent to elements during the bubble-up phase.(事件从目标节点向上冒泡派发给Visual Element的,即最里层符合的Visual Element最先派发(从下往上)(含响应事件最里层Visual Element))
  3. Cancellable: Events that can have their default action execution cancelled, stopped, or prevented.(事件是可取消的,比如一般的事件冒泡为了不继续想外层冒泡,我们会设置事件阻断防止继续冒泡)

EventPropagation

通过上面的介绍可以了解到,事件派发有两个流程(Trickles down & Bubbles up),在不打断事件派发的前提下,最里层响应Visual Element只会派发一次,其他外层传递路线上的Visual Element会派发两次事件。

事件基类为EventBase,所有相关事件介绍参考:

Event reference

Note:

  1. The UI Toolkit event system shares the same terminology and event naming as HTML events(UIToolkit采用和HTML一样的事件名和术语)

Handle Events

事件处理顺序如下:

  1. 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.(事件捕获阶段从上往下触发事件)
  2. Execute event callbacks on the event target. This is the target phase of the dispatch process.(在目标响应Visual Element上触发事件)
  3. Call ExecuteDefaultActionAtTarget() on the event target.(在目标响应Visual Element上触发ExecuteDefaultActionAtTarget()方法)
  4. 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开始向上冒泡触发事件)
  5. Call ExecuteDefaultAction() on the event target.(在目标响应Visual Element上触发ExecuteDefaultAction()方法)

事件响应里有两个重要的概念:

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
2
3
VisualElement myElement = new VisualElement();

myElement.RegisterCallback<MouseDownEvent>(MyCallback, TrickleDown.TrickleDown);

如果我们想事件传递自定义数据,我们需要自定义一个含自带数据的方法回调并监听事件:

1
2
3
myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData);

void MyCallbackWithData(MouseDownEvent evt, MyType data) { /* ... */ }

相关事件信息查询:

Event Reference

SerializedObject Data Binding

待添加……

UI Assets

Unity提供了两种Asset用于布局系统

  1. 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布局)
  2. 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

  1. UI Debugger(UI调试器—可视化查看UI节点详细数据)

    UIToolkitDebuggerPreview

  2. UI Builder(UI布局可视化编辑器(预览版本))

  3. UI Samples(UI事例)

    UIToolkitSampleWindow

实战

接下来通过实战学习,进一步了解UIToolkit的使用和设计。

基础学习

代码+USS创建UI

代码创建UI

UIToolkit为了帮助我们快速创建Editor UI窗口,提供了快捷创建入口:

Asset->Create->UI Toolkit->Editor Window

UIToolkitEditorWindowCreator

考虑到不适用UXML作为UI布局,使用纯代码写Editor UI,这里就不勾选UXML了。

创建完会看到生成一个.cs文件和一个.uss文件,这是因为我们勾选了C#和USS,接下来我们打开UICreateUIByCodeEditorWindow.cs开始我们的Editor UI代码编写,后续会讲到如何利用*.uss文件来指定UI风格。

UICreateUIByCodeEditorWindow.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class UICreateUIByCodeEditorWindow : EditorWindow
{
[MenuItem("Window/UI Toolkit/UIToolkitStudy/UICreateUIByCodeEditorWindow")]
public static void ShowUICodeStyleEditorWindow()
{

UICreateUIByCodeEditorWindow wnd = GetWindow<UICreateUIByCodeEditorWindow>();
wnd.titleContent = new GUIContent("UICreateUIByCodeEditorWindow");
}

/// <summary>
/// UIToolkit的rootVisualElement绘制方法
/// </summary>
public void CreateGUI()
{

CreateUIByCode();
}

/// <summary>
/// 通过代码创建UI
/// </summary>
private void CreateUIByCode()
{

// 创建一个ui容器,用于管理我们新增的ui组件
VisualElement uiContainer = new VisualElement();
// 添加一个标题作为ui容器标题组件显示
Label uiTitleLable = new Label("代码创建UI容器");
uiContainer.Add(uiTitleLable);

// 添加按钮组件
Button btn1 = new Button();
btn1.text = "代码创建按钮1";
uiContainer.Add(btn1);

// 必须将需要显示的组件添加到根节点才能显示
// 将ui容器添加到根节点作为需要显示的UIElement
rootVisualElement.Add(uiContainer);
}
}

UICreateUIByCodeEditorWindow

可以看到编写基于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
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 通过代码修改USS创建UI
/// </summary>
private void CreateStyleUIByCode()
{

******
// 给Label指定不同的Color Style
uiTitleLable.style.color = Color.red;
// 设置Label居中显示Style
uiTitleLable.style.alignSelf = Align.Center;
******
}

UIChangeUSSByCodeEditorWindow

可以看到通过访问style属性并修改其中的color属性,我成功的修改了标题文本的颜色显示风格。更多的属性修改参考文档:

IStyle

使用USS指定UI风格

第一步依然是使用Create->UI Toolkit->Editor WIndow

去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。

UIUseUSSEditorWindow.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class UIUseUSSEditorWindow : EditorWindow
{
[MenuItem("Window/UI Toolkit/UIToolkitStudy/UIUseUSSEditorWindow")]
public static void ShowUIUseUSSEditorWidnow()
{

UIUseUSSEditorWindow wnd = GetWindow<UIUseUSSEditorWindow>();
wnd.titleContent = new GUIContent("UIUseUSSEditorWindow");
}

public void CreateGUI()
{

CreateUIByUseUSS();
}

/// <summary>
/// 使用USS创建UI
/// </summary>
private void CreateUIByUseUSS()
{

VisualElement root = rootVisualElement;
// 加载并指定USS的使用
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Scripts/Editor/UIToolkitStudy/UIUseUSSEditorWindow/UIUseUSSEditorWindow.uss");
root.styleSheets.Add(styleSheet);

VisualElement 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");
root.Add(smallLabelWithStyle);
root.Add(bigLabelWithStyle);
root.Add(labelWithStyle);
root.Add(boldLabelWithStyle);

Button ussCenterButton = new Button();
ussCenterButton.name = "CenterButton";
ussCenterButton.text = "Center USS Button Name Selector";
root.Add(ussCenterButton);
}
}

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

可以看到我们在UIUseUSSEditorWindow.uss里分别定义了,Type Selector(Label)Class Selector(.SmallLabel, .BigLabel, .BoldLabel),Name Selector(CenterButton)

对应的我们在代码里通过以下三种方式分别指定了Selector:

  1. Type Selector

    1
    VisualElement labelWithStyle = new Label("USS Using Label Type Selector!");
  2. Class Selctor

    1
    2
    3
    4
    5
    6
    7
    VisualElement 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");
  3. Name Selector

    1
    2
    3
    Button ussCenterButton = new Button();
    ussCenterButton.name = "CenterButton";
    ussCenterButton.text = "Center USS Button Name Selector";
位置和排版

第一步依然是使用Create->UI Toolkit->Editor WIndow

去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。

UIPositionAndLayoutEditorWindow.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/// <summary>
/// 位置和排版EditorWindow
/// </summary>
public class UIPositionAndLayoutEditorWindow : EditorWindow
{
/// <summary>
/// 竖向相对位置根组件
/// </summary>
private VisualElement mRootVerticalRelativePosContainer;

[MenuItem("Window/UI Toolkit/UIToolkitStudy/UIPositionAndLayoutEditorWindow")]
public static void ShowUIPositionAndLayoutEditorWindow()
{

UIPositionAndLayoutEditorWindow wnd = GetWindow<UIPositionAndLayoutEditorWindow>();
wnd.titleContent = new GUIContent("UIPositionAndLayoutEditorWindow");
}

public void CreateGUI()
{

CreatePositionAndLayoutUI();
}

/// <summary>
/// 创建位置和排版UI
/// </summary>
private void CreatePositionAndLayoutUI()
{

CreateRootRelativePosVerticalContainer();
CreateHorizontalRelativePosContainer();
CreateHorzintalLayoutContainer();
CreateVerticalAbsolutePosContainer();
}

/// <summary>
/// 创建竖向相对位置根组件
/// </summary>
private void CreateRootRelativePosVerticalContainer()
{

// 竖向相对位置排版容器
mRootVerticalRelativePosContainer = new VisualElement();
// 设置竖向排版
mRootVerticalRelativePosContainer.style.flexDirection = FlexDirection.Column;
// 设置相对位置
mRootVerticalRelativePosContainer.style.position = Position.Relative;
// 指定竖向容器Style
mRootVerticalRelativePosContainer.AddToClassList("unity-box");
// 设置竖向容器大小位置
mRootVerticalRelativePosContainer.style.marginLeft = 10;
mRootVerticalRelativePosContainer.style.marginRight = 10;
mRootVerticalRelativePosContainer.style.marginTop = 10;
mRootVerticalRelativePosContainer.style.marginBottom = 10;
// 设置竖向容器自适应大小
mRootVerticalRelativePosContainer.style.flexGrow = 1;

// 添加竖向排版标签
Label verticalLabel = new Label();
verticalLabel.text = "竖向相对位置排版容器标题";
// 设置文本居中显示
verticalLabel.style.alignSelf = Align.Center;
mRootVerticalRelativePosContainer.Add(verticalLabel);
rootVisualElement.Add(mRootVerticalRelativePosContainer);
}

/// <summary>
/// 创建横向相对位置容器
/// </summary>
private void CreateHorizontalRelativePosContainer()
{

// 横向相对位置容器
var horizontalRelativePosContainer = new VisualElement();
// 设置横向排版
horizontalRelativePosContainer.style.flexDirection = FlexDirection.Row;
// 设置相对位置
horizontalRelativePosContainer.style.position = Position.Relative;
// 指定横向相对位置容器Style
horizontalRelativePosContainer.AddToClassList("unity-box");
// 添加横向相对位置标签
Label horizontalRelativePosLabel = new Label();
horizontalRelativePosLabel.text = "横向相对位置排版容器标题";
// 设置文本居中显示
horizontalRelativePosLabel.style.alignSelf = Align.Center;
horizontalRelativePosContainer.Add(horizontalRelativePosLabel);

// 设置横向容器多个按钮
Button horizontalButton1 = new Button();
horizontalButton1.text = "横向按钮1";
horizontalButton1.style.marginLeft = 25;
Button horizontalButton2 = new Button();
horizontalButton2.text = "横向按钮2";
horizontalButton2.style.marginLeft = 50f;
horizontalRelativePosContainer.Add(horizontalButton1);
horizontalRelativePosContainer.Add(horizontalButton2);

mRootVerticalRelativePosContainer.Add(horizontalRelativePosContainer);
}

/// <summary>
/// 创建横向排版容器
/// </summary>
private void CreateHorzintalLayoutContainer()
{

// 横向排版容器
var horizontalLayoutContainer = new VisualElement();
// 设置横向排版
horizontalLayoutContainer.style.flexDirection = FlexDirection.Row;
// 设置内容超出后排版Style
horizontalLayoutContainer.style.flexWrap = Wrap.Wrap;
// 指定横向排版容器Style
horizontalLayoutContainer.AddToClassList("unity-box");
// 添加横向排版标签
Label horizontalLayoutLabel = new Label();
horizontalLayoutLabel.text = "横向排版容器标题";
// 设置文本居中显示
horizontalLayoutLabel.style.alignSelf = Align.Center;
horizontalLayoutContainer.Add(horizontalLayoutLabel);

// 设置横向容器多个按钮
Button horizontalButton1 = new Button();
horizontalButton1.text = "横向按钮1";
// 设置自动扩展系数
horizontalButton1.style.flexGrow = 1;
Button horizontalButton2 = new Button();
horizontalButton2.text = "横向按钮2";
// 设置自动扩展系数
horizontalButton2.style.flexGrow = 2;
// 设置偏移间隔
horizontalButton2.style.marginLeft = 10;
Button horizontalButton3 = new Button();
horizontalButton3.text = "横向按钮3";
// 设置自动扩展系数
horizontalButton3.style.flexGrow = 3;
// 设置偏移间隔
horizontalButton3.style.marginLeft = 10;
Button horizontalButton4 = new Button();
horizontalButton4.text = "横向按钮4";
// 设置自动扩展系数
horizontalButton4.style.flexGrow = 4;
// 设置偏移间隔
horizontalButton4.style.marginLeft = 10;
horizontalLayoutContainer.Add(horizontalButton1);
horizontalLayoutContainer.Add(horizontalButton2);
horizontalLayoutContainer.Add(horizontalButton3);
horizontalLayoutContainer.Add(horizontalButton4);

mRootVerticalRelativePosContainer.Add(horizontalLayoutContainer);
}

/// <summary>
/// 创建竖向绝对位置容器
/// </summary>
private void CreateVerticalAbsolutePosContainer()
{

// 竖向绝对坐标容器
var verticalAbsolutePosContainer = new VisualElement();
// 设置竖向排版
verticalAbsolutePosContainer.style.flexDirection = FlexDirection.Column;
// 设置绝对坐标
verticalAbsolutePosContainer.style.position = Position.Absolute;
// 指定竖向绝对位置容器Style
verticalAbsolutePosContainer.AddToClassList("unity-box");
// 设置竖向容器大小位置
verticalAbsolutePosContainer.style.left = 100;
verticalAbsolutePosContainer.style.right = 100;
verticalAbsolutePosContainer.style.top = 100;
verticalAbsolutePosContainer.style.bottom = 100;
// 设置竖向绝对位置容器自适应
verticalAbsolutePosContainer.style.flexGrow = 1;

// 添加竖向排版标签
Label verticalAbsolutePosLabel = new Label();
verticalAbsolutePosLabel.text = "竖向绝对位置标题";
// 设置文本居中显示
verticalAbsolutePosLabel.style.alignSelf = Align.Center;
verticalAbsolutePosContainer.Add(verticalAbsolutePosLabel);

// 设置竖向容器多个按钮
Button verticalButton1 = new Button();
verticalButton1.text = "竖向按钮1";
// 设置按钮高度和位置
verticalButton1.style.height = 20;
verticalButton1.style.marginLeft = 20;
verticalButton1.style.marginRight = 20;
Button verticalButton2 = new Button();
verticalButton2.text = "竖向按钮2";
// 设置按钮高度
verticalButton2.style.height = 20;
verticalButton2.style.marginLeft = 20;
verticalButton2.style.marginRight = 20;
verticalAbsolutePosContainer.Add(verticalButton1);
verticalAbsolutePosContainer.Add(verticalButton2);

mRootVerticalRelativePosContainer.Add(verticalAbsolutePosContainer);
}
}

UIPositionAndLayoutEditorWindow

可以看到通过不断创建需要排版的容器设置对应排版和大小属性,我们成功的创建出了各种排版方向以及相对位置和绝对位置的显示UI。

自定义USS变量

在创建USS文件时,我们很多时候会填充相同值给不同的Selector,而修改时并不想一个一个去修改,这个时候就需要用到类似编程上定义变量公用同一个变量的方式。

第一步依然是使用Create->UI Toolkit->Editor WIndow

去掉UXML勾选,这里依然只通过代码和USS文件创建UI显示。

UICustomUSSVariableEditorWindow.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

/// <summary>
/// 创建自定义USS变量的Editor Window
/// </summary>
public class UICustomUSSVariableEditorWindow : EditorWindow
{
[MenuItem("Window/UI Toolkit/UIToolkitStudy/UICustomUSSVariableEditorWindow")]
public static void ShowUICustomUSSVariableEditorWindow()
{

UICustomUSSVariableEditorWindow wnd = GetWindow<UICustomUSSVariableEditorWindow>();
wnd.titleContent = new GUIContent("UICustomUSSVariableEditorWindow");
}

public void CreateGUI()
{

CreateCustomUSSVariableUI();
}

/// <summary>
/// 创建自定义USS变量的UI
/// </summary>
private void CreateCustomUSSVariableUI()
{

// 加载并指定USS的使用
var customUSSVariable1StyleSheet1 = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Scripts/Editor/UIToolkitStudy/UICustomUSSVariableEditorWindow/UICustomUSSVariableEditorWindow1.uss");
var customUSSVariable1StyleSheet2 = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Scripts/Editor/UIToolkitStudy/UICustomUSSVariableEditorWindow/UICustomUSSVariableEditorWindow2.uss");
rootVisualElement.styleSheets.Add(customUSSVariable1StyleSheet1);
rootVisualElement.styleSheets.Add(customUSSVariable1StyleSheet2);

// 使用UICustomUSSVariableEditorWindow1里的NormalLabel
Label normalLabel = new Label();
normalLabel.AddToClassList("NormalLabel");
normalLabel.text = "NormalLabel1";
normalLabel.style.left = 10;
normalLabel.style.right = 10;
normalLabel.style.height = 20;


// 使用UICustomUSSVariableEditorWindow2里的NormalLabel2
Label normalLabel2 = new Label();
normalLabel2.AddToClassList("NormalLabel2");
normalLabel2.text = "NormalLabel2";
normalLabel2.style.left = 10;
normalLabel2.style.right = 10;
normalLabel2.style.height = 20;

rootVisualElement.Add(normalLabel);
rootVisualElement.Add(normalLabel2);
}
}

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;
}

UICustomUSSVariableEditorWindow

可以看到我们在USS文件里自定义了变量并在Class Selector里使用,通过加载多个USS文件,我们实现了跨USS文件的变量定义访问使用。

UXML创建UI

待添加……

UI事件响应

数据绑定

调试

知识点

  1. EditorWindow.CreateGUI()方法是用于UIToolkit创建Editor窗口的rootVisualElement显示方法
  2. Editor.CreateInspectorGUI()方法是用于UIToolkit创建自定义Inspector面板的显示方法
  3. 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:

UnityEditor知识

Reference

Creating user interfaces (UI)

CSS

USS properties reference

IStyle

UXML现有可用Element查询

USS built-in variable references

Event Reference

文章目錄
  1. 1. Introduction
  2. 2. UIToolkit
    1. 2.1. UI system
      1. 2.1.1. Visual tree
      2. 2.1.2. Layout Engine
      3. 2.1.3. UXML format
      4. 2.1.4. Uity style sheets(USS)
        1. 2.1.4.1. USS Selector
          1. 2.1.4.1.1. Type selector
          2. 2.1.4.1.2. Class selector
          3. 2.1.4.1.3. Name selector
          4. 2.1.4.1.4. Universal selector
        2. 2.1.4.2. USS properties
          1. 2.1.4.2.1. USS data types
          2. 2.1.4.2.2. USS common properties
            1. 2.1.4.2.2.1. 宽高属性
            2. 2.1.4.2.2.2. 边缘(Margins)属性
            3. 2.1.4.2.2.3. 边界(Border)属性
            4. 2.1.4.2.2.4. 内边距(Padding)属性
            5. 2.1.4.2.2.5. 排版属性
            6. 2.1.4.2.2.6. 位置属性
            7. 2.1.4.2.2.7. 背景属性
            8. 2.1.4.2.2.8. 九宫属性
            9. 2.1.4.2.2.9. Appearance(外观)属性
            10. 2.1.4.2.2.10. 文本属性
        3. 2.1.4.3. USS custom properties
        4. 2.1.4.4. Best practices for USS
        5. 2.1.4.5. Theme Style Sheet(TSS)
      5. 2.1.5. Query Elements
      6. 2.1.6. Event System
        1. 2.1.6.1. Dispatch Events
        2. 2.1.6.2. Handle Events
      7. 2.1.7. SerializedObject Data Binding
    2. 2.2. UI Assets
    3. 2.3. UI Tools and resources
    4. 2.4. 实战
      1. 2.4.1. 基础学习
        1. 2.4.1.1. 代码+USS创建UI
          1. 2.4.1.1.1. 代码创建UI
          2. 2.4.1.1.2. 代码修改USS
          3. 2.4.1.1.3. 使用USS指定UI风格
          4. 2.4.1.1.4. 位置和排版
          5. 2.4.1.1.5. 自定义USS变量
        2. 2.4.1.2. UXML创建UI
        3. 2.4.1.3. UI事件响应
        4. 2.4.1.4. 数据绑定
        5. 2.4.1.5. 调试
        6. 2.4.1.6. 知识点
      2. 2.4.2. 节点编辑器实现
  3. 3. Github
  4. 4. Reference