找回密码
 立即注册
查看: 280|回复: 0

Unity UI Toolkit

[复制链接]
发表于 2023-4-3 09:24 | 显示全部楼层 |阅读模式
一.简介

1、两个文件

.UXML :界面信息文件,保存了用户界面的逻辑结构


<ui:UXML
         xmlns:ui="UnityEngine.UIElements"
         xmlns:uie="UnityEditor.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance"
         engine="UnityEngine.UIElements"
         editor="UnityEditor.UIElements"
         noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
         editor-extension-mode="False">
    <!--USS文件,保存资源路径+GUID,编辑器模式下均通过路径进行加载-->
    <Style src="project://database/Assets/CustomUss1.uss?fileID=7433441132597879392&guid=14c2bcd9a5eefc24889c0596e1209921&type=3#CustomUss1" />
   
    <!--ui:VisualElement(标签:类型为VisualElement), style(属性,此处为背景图片) = “xxx”(属性值)-->
    <ui:VisualElement style="background-image: url('project://database/Assets/numthreads%E7%A4%BA%E4%BE%8B.jpg?fileID=2800000&guid=af712550a72825747b7d66b740e7d1a0&type=3#numthreads示例');">
        
        <ui:Label text="Label" display-tooltip-when-elided="true" />
    </ui:VisualElement>
    <ui:Label text="Label" display-tooltip-when-elided="true" />
</ui:UXML>
.USS: 样式选择器



2、一个类型

VisualElement:所有UI元素的基类,元素采用相对坐标和绝对坐标的机制在布局中排开。在UI Toolkit中,每个VisualElement都可以进行嵌套,子对象将按照父对象的设置进行布局。
如何获取一个控件对象:



public class NewBehaviourScript : MonoBehaviour
{
    void Start()
    {
        UIDocument document = GetComponent<UIDocument>();
        var root = document.rootVisualElement;

        Button button = root.Q<Button>("MyBtn");

        button.clicked += () =>
            {
            Debug.Log("我被点了");
            };
        
        button.RegisterCallback<PointerOutEvent>((evt) =>
            {
            Debug.Log("鼠标挪走了");
            });
    }
}
如何自定义一个VisualElement:
public class StatusBar : VisualElement
{
    //UI资源路径
    private const string UXML_PATH = "Assets/status.uxml";
   
    //工厂:使UI工具包在读取UXML文件时能够实例化对应类型
    public new class UxmlFactory : UxmlFactory<StatusBar, UxmlTraits> { }
   
    //特征类:UXML文件中的定义
    public new class UxmlTraits : VisualElement.UxmlTraits
    {
        //创建xml文件中属性类实例
        UxmlStringAttributeDescription _status = new UxmlStringAttributeDescription { name = "statusww" };
        UxmlStringAttributeDescription _testStr = new UxmlStringAttributeDescription { name = "TestStr" };
        public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
        {
            base.Init(ve, bag, cc);
            StatusBar statusBar = ve as StatusBar;
            //属性赋值
            statusBar.status = _status.GetValueFromBag(bag, cc);
            statusBar.TestStr = _testStr.GetValueFromBag(bag, cc);
        }
    }
    public string status { get; set; }

    private Label _testStr;
    private UnityEngine.UIElements.Button _button;
   
    private int  _num = 0;
    private string _status;

    public string TestStr
    {
        get { return _testStr.text; }
        set { _testStr.text = value; }
    }
   
    //必须提供一个默认构造函数
    public StatusBar()
    {
        _status = String.Empty;
        //加载该类型对应的VisualTree
        VisualTreeAsset template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UXML_PATH);
        template.CloneTree(this);
        Init();
    }

    public void Init()
    {
        //绑定组件
        _testStr = this.Q<Label>("testStr");
        _button = this.Q<UnityEngine.UIElements.Button>("testBtn");
        TestStr = _num.ToString();
        _button.clicked += _BtnOnClick;
    }

    public void UnInit()
    {
        
    }

    private void _BtnOnClick()
    {
        TestStr = Random.Range(0, 100).ToString();
    }
}

二.原理

前置:

IMGUI:

每帧遍历布局树,对每个UI元素设置渲染状态、生成网格顶点、执行DrawCall。
UGUI:

在界面改变时生成网格顶点,并基于Canvas进行合批,将材质相同可以合批的网格进行合批
1、UI Toolkit DrawCall合并策略:

策略:将每个参与渲染的VisualElement放在队列中,直到队列中出现第九个与之前元素使用的不同的Texture或Atlas,之前的元素被合并为一个DrawCall
UI Toolkit并不关心图集完全依据界面布局进行DrawCall合并,UI Toolkit 将所有几何数据保存在一个Vb/IB(顶点缓冲区/顶点索引缓冲区)中 , 底层使用UberShader(全能着色器:在Shader中使用宏定义来提高着色器的复用率,执行分支),这样可以将原先在CPU的计算损耗转移到GPU中。对于在界面中运行时可能会发生变化的元素,UIR引入GPU分配器来管理 来自VB/IB的顶点/索引分配,如果元素变化就在同一个VB/IB中分配一个新的控件来填充他的新顶点/索引,其元素不变,并且保证渲染顺序。
2、对比测试

IMGUI与UI Toolkit




IMGUI



UI Toolkit

UGUI 与 UI Toolkit





UGUI Profiler



UGUI FrameDebugger



UI Toolkit Profiler


UI Toolkit
3、结论:

新一代UI Toolkit在性能方面有很大的提升,具体表现在以下几个方面
(1)使用UXML文件 来保存界面布局信息

在UGUI中以GameObject -> Prefab的形式来保存一份UI界面数据,通常一个控件对应的Prefab将会保存:GameObject 、 RectTransfrom、 CanvasRendered、 MonoBehaviour 这些数据,这其中大部分数据都并不需要并且就算不改变颜色、位置等信息,这部分数据也需要保存。而在UXML中精简了这部分数据结构,只保存最精简的描述信息.同时这种方式存储界面信息也更容易进行UI界面的迁移。



<!--XML声明,可选,如果使用声明则必须在第第一行,且前面不能出现其他任何内容包括空格-->
<?xml version="1.0" encoding="utf-8"?>
<ui:UXML
         xmlns:ui="UnityEngine.UIElements"
         xmlns:uie="UnityEditor.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance"
         engine="UnityEngine.UIElements"
         editor="UnityEditor.UIElements"
         noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
         editor-extension-mode="False">
    <!--当前界面所包含的元素 及对应属性-->
    <ui:Label text="Label" display-tooltip-when-elided="true" class="test2" />
    <ui:Button text="Button" display-tooltip-when-elided="true" />
</ui:UXML>(2)结合UGUI和SpriteRenderer 的DrawCall合批

使用者可以不再关心图集,UI Toolkit中完全按照界面布局的方式进行合批,能容纳8张Texture和一个 Font Texture。平衡了CPU和GPU的开销能够大量减少DC。这都得益于UberShader 对渲染状态切换的支持。
三.适用方向

1.编辑器页面

通过UIBuilder可以简洁直观的绘制出应用UI界面,无需代码策划也可以参与到UI的设计与绘制。并且由于DrawCall的优化使用UI Toolkit代替 IMGUI可以大大提高编辑时性能
2.简单的Runtime UI界面

由于目前UI Toolkit 还有很多未解决的问题:UI和粒子特效叠层, UI和3D模型叠层,3D界面、K帧动画等所以 UI Toolkit只能用来做一些简单无特效的UI应用界面。同时UI Toolkit支持与UGUI的混用,可以在一些纯粹的UI界面使用这种技术。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-16 10:11 , Processed in 0.108483 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表