UI Toolkit系统学习

news2024/9/20 12:40:10

UI Toolkit
此文章用于学习UnityUI系统,手头的项目做完会来完善
官方文档
Unity上方菜单栏点击Window->UI Toolkit->Samples可以看UI Toolkit中的很多样例

使用 UI Toolkit 和 UI Builder 制作物品编辑器

在文件夹中右键->Create->UI Toolkit->Editor Window
出现以下窗口
在这里插入图片描述
输入名字创建窗口
文件夹中出现三个文件
在这里插入图片描述
从这步开始就能点击Unity上方菜单栏召唤自己写的Editor Window了

点击ItemEditor进入UIBuilder,用里面的工具创建需要的UI面板
在这里插入图片描述

创建 ListView 中的 ItemTemplate

再创建一个UIDocument,作为ItemList中每一个物体显示的模板
在这里插入图片描述

生成 ListView 列表

实现ItemTemplate读取数据库中物品数据并将其呈现在ListView上的方法

public class ItemEditor : EditorWindow
{
    private ItemDataList_SO dataBase;
    private List<ItemDetails> itemList = new List<ItemDetails>();
    private VisualTreeAsset itemRowTemplate;
    private ListView itemListView;
    
    [MenuItem("Editor/ItemEditor")]
    public static void ShowExample()
    {
        ItemEditor wnd = GetWindow<ItemEditor>();
        wnd.titleContent = new GUIContent("ItemEditor");
    }

    public void CreateGUI()
    {
       
        VisualElement root = rootVisualElement;
        var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/UIBuilder/ItemEditor.uxml");
        VisualElement labelFromUXML = visualTree.Instantiate();
        root.Add(labelFromUXML);
        
        //拿到模板数据
        itemRowTemplate =
            AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/UIBuilder/ItemRowTemplate.uxml");
        //变量赋值
        itemListView = root.Q<VisualElement>("ItemList").Q<ListView>("ListView");
        LoadDataBase();
        GenerateListView();
    }
    /// <summary>
    /// 从数据库中加载文件
    /// </summary>
    private void LoadDataBase()
    {
        var dataArray=AssetDatabase.FindAssets("ItemDataList_SO");
        if (dataArray.Length > 1)
        {
            var path = AssetDatabase.GUIDToAssetPath(dataArray[0]);//找到文件中对应的路径
            dataBase = AssetDatabase.LoadAssetAtPath(path, typeof(ItemDataList_SO)) as ItemDataList_SO;
        }

        itemList = dataBase.itemDetailsList;
        //如果不标记则无法保存数据
        EditorUtility.SetDirty(dataBase);
    }

    private void GenerateListView()
    {
        Func<VisualElement> makeItem = () => itemRowTemplate.CloneTree();//创建
        Action<VisualElement, int> bindItem = (e, i) =>
        {//在ItemTemplate上绑定数据
            if (i < itemList.Count)
            {
                if (itemList[i].itemIcon != null)
                {
                    e.Q<VisualElement>("Icon").style.backgroundImage = itemList[i].itemIcon.texture;
                   
                }
                e.Q<Label>("Name").text = itemList[i] == null ? "NO ITEM" : itemList[i].itemName;
            }
        };
        //将ItemTemplate呈现在ListView里
        itemListView.fixedItemHeight = 60;
        itemListView.itemsSource = itemList;
        itemListView.makeItem = makeItem;
        itemListView.bindItem = bindItem;
    }
}

makeItem会在每次添加新物品时调用——在数据绘制到窗口时会根据数据的数目逐一增加一个Item的时候
bindItem每个窗口显示的具体内容
itemsSource对应itemList里面的每一个数据

绑定 Editor Window 中的参数变量,实现 ListView 添加删除同步信息功能

实现在右侧面板中显示物体对应的信息

public class ItemEditor : EditorWindow
{
    private ItemDataList_SO dataBase;
    private List<ItemDetails> itemList = new List<ItemDetails>();
    private VisualTreeAsset itemRowTemplate;
    private ScrollView itemDetailsSection;
    private ItemDetails activeItem;

    //默认预览图片
    private Sprite defaultIcon;

    private VisualElement iconPreview;
    //获得VisualElement
    private ListView itemListView;

    [MenuItem("M STUDIO/ItemEditor")]
    public static void ShowExample()
    {
        ItemEditor wnd = GetWindow<ItemEditor>();
        wnd.titleContent = new GUIContent("ItemEditor");
    }

    public void CreateGUI()
    {
        // Each editor window contains a root VisualElement object
        VisualElement root = rootVisualElement;

        // VisualElements objects can contain other VisualElement following a tree hierarchy.
        // VisualElement label = new Label("Hello World! From C#");
        // root.Add(label);

        // Import UXML
        var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/UI Builder/ItemEditor.uxml");
        VisualElement labelFromUXML = visualTree.Instantiate();
        root.Add(labelFromUXML);

        //拿到模版数据
        itemRowTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/UI Builder/ItemRowTemplate.uxml");

        //拿默认Icon图片
        defaultIcon = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/M Studio/Art/Items/Icons/icon_M.png");

        //变量赋值
        itemListView = root.Q<VisualElement>("ItemList").Q<ListView>("ListView");
        itemDetailsSection = root.Q<ScrollView>("ItemDetails");
        iconPreview = itemDetailsSection.Q<VisualElement>("Icon");


        //获得按键
        root.Q<Button>("AddButton").clicked += OnAddItemClicked;
        root.Q<Button>("DeleteButton").clicked += OnDeleteClicked;
        //加载数据
        LoadDataBase();

        //生成ListView
        GenerateListView();
    }

    #region 按键事件
    private void OnDeleteClicked()
    {
        itemList.Remove(activeItem);
        itemListView.Rebuild();
        itemDetailsSection.visible = false;
    }

    private void OnAddItemClicked()
    {
        ItemDetails newItem = new ItemDetails();
        newItem.itemName = "NEW ITEM";
        newItem.itemID = 1001 + itemList.Count;
        itemList.Add(newItem);
        itemListView.Rebuild();
    }
    #endregion

    private void LoadDataBase()
    {
        var dataArray = AssetDatabase.FindAssets("ItemDataList_SO");
        //var dataArray = AssetDatabase.FindAssets("t:ItemDataList_SO");  //不同版本写法不一样
        //if (dataArray.Length >= 1)    //不同版本写法不同
        if (dataArray.Length > 1)
        {
            var path = AssetDatabase.GUIDToAssetPath(dataArray[0]);
            dataBase = AssetDatabase.LoadAssetAtPath(path, typeof(ItemDataList_SO)) as ItemDataList_SO;
        }

        itemList = dataBase.itemDetailsList;
        //如果不标记则无法保存数据
        EditorUtility.SetDirty(dataBase);
        // Debug.Log(itemList[0].itemID);
    }

    private void GenerateListView()
    {
        Func<VisualElement> makeItem = () => itemRowTemplate.CloneTree();

        Action<VisualElement, int> bindItem = (e, i) =>
        {
            if (i < itemList.Count)
            {
                if (itemList[i].itemIcon != null)
                    e.Q<VisualElement>("Icon").style.backgroundImage = itemList[i].itemIcon.texture;
                e.Q<Label>("Name").text = itemList[i] == null ? "NO ITEM" : itemList[i].itemName;
            }
        };

        itemListView.fixedItemHeight = 50;  //根据需要高度调整数值
        itemListView.itemsSource = itemList;
        itemListView.makeItem = makeItem;
        itemListView.bindItem = bindItem;

        itemListView.onSelectionChange += OnListSelectionChange;

        //右侧信息面板不可见
        itemDetailsSection.visible = false;
    }

    private void OnListSelectionChange(IEnumerable<object> selectedItem)
    {
        activeItem = (ItemDetails)selectedItem.First();
        GetItemDetails();
        itemDetailsSection.visible = true;
    }

    private void GetItemDetails()
    {
        itemDetailsSection.MarkDirtyRepaint();

        itemDetailsSection.Q<IntegerField>("ItemID").value = activeItem.itemID;
        itemDetailsSection.Q<IntegerField>("ItemID").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemID = evt.newValue;
        });

        itemDetailsSection.Q<TextField>("ItemName").value = activeItem.itemName;
        itemDetailsSection.Q<TextField>("ItemName").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemName = evt.newValue;
            itemListView.Rebuild();
        });

        iconPreview.style.backgroundImage = activeItem.itemIcon == null ? defaultIcon.texture : activeItem.itemIcon.texture;
        itemDetailsSection.Q<ObjectField>("ItemIcon").value = activeItem.itemIcon;
        itemDetailsSection.Q<ObjectField>("ItemIcon").RegisterValueChangedCallback(evt =>
        {
            Sprite newIcon = evt.newValue as Sprite;
            activeItem.itemIcon = newIcon;

            iconPreview.style.backgroundImage = newIcon == null ? defaultIcon.texture : newIcon.texture;
            itemListView.Rebuild();
        });

        //其他所有变量的绑定
        itemDetailsSection.Q<ObjectField>("ItemSprite").value = activeItem.itemOnWorldSprite;
        itemDetailsSection.Q<ObjectField>("ItemSprite").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemOnWorldSprite = (Sprite)evt.newValue;
        });

        itemDetailsSection.Q<EnumField>("ItemType").Init(activeItem.itemType);
        itemDetailsSection.Q<EnumField>("ItemType").value = activeItem.itemType;
        itemDetailsSection.Q<EnumField>("ItemType").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemType = (ItemType)evt.newValue;
        });

        itemDetailsSection.Q<TextField>("Description").value = activeItem.itemDescription;
        itemDetailsSection.Q<TextField>("Description").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemDescription = evt.newValue;
        });

        itemDetailsSection.Q<IntegerField>("ItemUseRadius").value = activeItem.itemUseRadius;
        itemDetailsSection.Q<IntegerField>("ItemUseRadius").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemUseRadius = evt.newValue;
        });

        itemDetailsSection.Q<Toggle>("CanPickedup").value = activeItem.canPickedup;
        itemDetailsSection.Q<Toggle>("CanPickedup").RegisterValueChangedCallback(evt =>
        {
            activeItem.canPickedup = evt.newValue;
        });

        itemDetailsSection.Q<Toggle>("CanDropped").value = activeItem.canDropped;
        itemDetailsSection.Q<Toggle>("CanDropped").RegisterValueChangedCallback(evt =>
        {
            activeItem.canDropped = evt.newValue;
        });

        itemDetailsSection.Q<Toggle>("CanCarried").value = activeItem.canCarried;
        itemDetailsSection.Q<Toggle>("CanCarried").RegisterValueChangedCallback(evt =>
        {
            activeItem.canCarried = evt.newValue;
        });

        itemDetailsSection.Q<IntegerField>("Price").value = activeItem.itemPrice;
        itemDetailsSection.Q<IntegerField>("Price").RegisterValueChangedCallback(evt =>
        {
            activeItem.itemPrice = evt.newValue;
        });

        itemDetailsSection.Q<Slider>("SellPercentage").value = activeItem.sellPercentage;
        itemDetailsSection.Q<Slider>("SellPercentage").RegisterValueChangedCallback(evt =>
        {
            activeItem.sellPercentage = evt.newValue;
        });
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1870412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

上午写的博客,下午就上了bing首页,惊不惊喜,意不意外

今天上午写了一篇《用免费的“山水博客”来管理你的离线文章》的博客&#xff0c;没想到下午在必应就搜到了&#xff0c;而且还是首页第四个。 不由的让人感概&#xff0c;以前写了那么多的博客&#xff0c;想将排名排前点&#xff0c;在网上找了不少秘籍&#xff0c;都没成功&…

昇思25天学习打卡营第4天|数据集Dataset

数据集 Dataset 介绍 之前说过&#xff0c;MindSpore是基于Pipeline&#xff0c;通过Dataset和Transformer进行数据处理。Dataset在其中是用来加载原始数据的。mindSpore提供了数据集加载接口&#xff0c;可以加载文本、图像、音频等&#xff0c;同时也可以自定义加载接口。此…

算法刷题日志 —— 数组和位运算

文章目录 [461. 汉明距离](https://leetcode.cn/problems/hamming-distance/submissions/542447020/)[448. 找到所有数组中消失的数字](https://leetcode.cn/problems/find-all-numbers-disappeared-in-an-array/submissions/)[136. 只出现一次的数字](https://leetcode.cn/pro…

中小企业的数字化转型业务场景落地案例

引言&#xff1a;随着商业活动的复杂化和全球化程度的提高&#xff0c;合同作为商业交易的重要组成部分&#xff0c;其数量、条款和复杂性都在不断增加。企业面临着越来越多的合同管理挑战&#xff0c;包括合同数量增多、条款繁琐、文件分散存储等问题。而中小企业由于管理不到…

食品安全管理员2024年全国通用考试题库资料。

61.产品的最小销售单元&#xff0c;其包装的表面积若小于&#xff08;&#xff09;cm2&#xff0c;可以仅标注产品名称、生产者名称和生产日期 A.10 B.15 答案&#xff1a;A 62.禁在猪饲料中添加各类"瘦肉精"。常见的"瘦肉精"是(  )。 A.苯甲酸钠 …

日元跌破160大关,日本当局何时干预?

KlipC报道&#xff1a;6月26日&#xff0c;日元又跌了&#xff0c;美元兑日元跌破160的整关口&#xff0c;超过了4月日本官员在市场上干预的水平&#xff0c;创1986年来新低。美联储降息的可能性降低&#xff0c;市场预计日元有可能延续当前的弱势。 KlipC分析师David表示&…

短视频带货实战营(高阶课),从0到1做个赚钱的抖音号(17节课)

课程目录&#xff1a; 1-短视频带贷先导课_1.mp4 2-账号搭建_1.mp4 3-账号养号涨粉套路_1.mp4 4-开通橱窗_1.mp4 5-管家式选品_1.mp4 6-六个能赚钱的赛道_1.mp4 7-选品之精选联盟_1.mp4 8-好物分享的三种形式_1.mp4 9-短视频之图文课_1.mp4 10-短视频之剪辑课_1.mp4 …

【软件测试】白盒测试与接口测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&a…

“所得”如何超越“所见”?合合信息扫描全能王发布扫描“黑科技”

数字化时代&#xff0c;扫描工具正变得越来越智能和个性化。除了常规的文件扫描外&#xff0c;无论是在工作中&#xff0c;还是在旅途里&#xff0c;人们也经常会使用手机扫描褶皱、脏污的文件&#xff0c;或是旅行交通地图、博物馆展品介绍等&#xff0c;打造独属于自己的随身…

上市公司银行专利申请数据集(2003-2022年)

数据简介&#xff1a;上市商业银行的专利申请数据是可作为金融科技创新水平的关键指标&#xff0c;这些数据反映了银行在金融技术领域的创新能力。发明专利因其创新性、技术深度和行业代表性&#xff0c;被赋予了特别的重视。遵循郭晔等人(2022)的研究方法&#xff0c;使用国家…

MyBatis~配置解析, 属性(properties)、设置(settings)

注意, 对应的名称一定要相同, 比如username就要对应username, 而且如果同时使用外部配置文件和property, 优先级是外部配置文件优先级更高 设置&#xff08;settings&#xff09; 这是 MyBatis 中极为重要的调整设置&#xff0c;它们会改变 MyBatis 的运行时行为。 下表描述了…

基于盲信号处理的声音分离——基于自然梯度的ICA算法

基于自然梯度的ICA算法主要利用相互独立的随机信号的联合概率密度是各自概率密度的乘积这一特性&#xff0c;建立了等独立性度量原则&#xff0c;具体实现如下。 首先&#xff0c;输出信号 相互独立&#xff0c;则其概率密度满足 上式中 表示 的概率密度函数&#xff0c;可以…

python FastAPI操作数据库实现注册登录

代码如下 from fastapi import FastAPI, APIRouter, HTTPException, status from pydantic import BaseModel from fastapi.responses import JSONResponse from typing import Optional from fastapi.middleware.cors import CORSMiddleware from utils.time import DateTime…

消失的80后都去哪里了

曾经被贴上各种标签的80后&#xff0c;最大的已经44岁&#xff0c;最小的也都35岁了&#xff0c;都已人到中年了。 在80后眼里的弟弟妹妹的90后&#xff0c;已经奔四了&#xff0c;而觉得与80后有代差的95后已是职场主力&#xff0c;而某些80后的孩子00后也已经开始陆续进入职场…

【系统架构设计师】计算机组成与体系结构 ③ ( 层次化存储结构 | 寄存器 | 高速缓存 | 内存 | 外存 )

文章目录 一、层次化存储结构1、层次化存储结构2、层次化存储结构 - 示例说明3、程序员可操作的部分 计算机 采用 分级存储结构 , 主要目的是 为了 解决 容量 / 价格 / 速度 之间的矛盾 ; 一、层次化存储结构 1、层次化存储结构 计算机 存储器 按照存储速度 由快到慢 进行排序 …

关于sum+=1与sum=sum+1的关系(C语言)

一、sum 1;与sum sum 1;是相等的&#xff0c;运算结果相等&#xff1b; 二、用一段代码说明&#xff1b; # define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int main() {//初始变量值&#xff1b;int n1 1;int n2 1;//输出&#xff1b;printf("运算前的n…

02逻辑代数与硬件描述语言基础

2.1 逻辑代数&#xff08;简单逻辑的运算&#xff09; 2.2 逻辑函数的卡诺图&#xff08;从图论的角度&#xff09;化简法 2.3 硬件描述语言Verilog HDL基础&#xff08;研究生阶段才用得到&#xff09; 要求&#xff1a; 1、熟悉逻辑代数常用基本定律、恒等式和规则。 2、掌握…

电脑文件concrt140.dll丢失要怎么恢复?靠谱修复方法分析

电脑文件concrt140.dll丢失这种情况&#xff0c;相对来说还是比较少见的&#xff01;但是不代表没有&#xff0c;既然有人出现这种情况了&#xff0c;那么小编势必要给大家详细的讲解一下concrt140.dll这个文件&#xff0c;以及我们要怎么去解决concrt140.dll文件丢失的问题。下…

龙国南方航空滑块acw_v2+cookie+风控处理+type后缀

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未经许可禁…

【Linux】进程优先级 | 环境变量

目录 Ⅰ. 进程优先级&#xff08;Process Priority&#xff09; 1. 什么是进程优先级&#xff1f; 2. 查看系统进程 3. 修改进程优先级 4.优先级调度原理 Ⅱ. 进程的切换&#xff08;Process Switch&#xff09; 1. 竞争与独立 2. 并行与并发 3. 进程抢占 4.实现切换…