Unity3D 资源引用列表

news2024/11/14 14:01:19

Unity3D 窗口绘制资源引用列表。

资源引用列表

我们可以在自定义窗口上绘制一个资源引用列表,筛选资源,点击引用,快速定位到资源文件夹的某个资源。

关于自定义窗口的基本实现,可以参考之前的文章《Unity3D 自定义窗口》。

获取资源引用

在 Editor 文件夹中,创建脚本 ResourceReference.cs

参考之前的文章《Unity3D 遍历预制体》,遍历 Assets 文件夹下的预制体,获取一些资源引用,存储到 objects 列表中。

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;

public class ResourceReference : EditorWindow
{
    public static string path = "Assets/";
    public static List<Object> objects = new List<Object>();

    [MenuItem("搜索工具/资源引用列表")]
    public static void OpenWindow()
    {
        // 创建窗口对象
        ResourceReference window = GetWindow<ResourceReference>();

        // 设置窗口标题
        window.titleContent = new GUIContent("资源引用列表");

        // 显示窗口
        window.Show();
    }

    void OnGUI()
    {
        GUILayout.Label("自定义搜索工具,可以遍历资源并修改");

        GUILayout.Space(10);

        path = EditorGUILayout.TextField("根目录", path);

        GUILayout.Space(10);

        if (GUILayout.Button("确定", GUILayout.Height(50)))
        {
            objects.Clear();
            TraversePrefab();
        }
    }

    public static void TraversePrefab()
    {
        var allfiles = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);

        foreach (var file in allfiles)
        {
            GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(file);
            if (go)
            {
                Debug.Log(go);
                objects.Add(go);
            }
        }
    }
}

点击菜单栏按钮,打开窗口。

菜单栏按钮

点击窗口内的按钮,可以看到打印的信息,此时资源已经被添加到 objects 列表中,暂时看不到。

搜索预制体

注意:每次点击按钮时,要调用 objects.Clear() 清空列表,防止重复添加。

绘制单个资源引用

使用 EditorGUILayout.ObjectField 绘制一个资源引用,传入需要显示的 object 以及它可以被赋值的类型(直接用它本身的类型 Object 即可),第三个布尔参数表示可以赋值场景中的物体。

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;

public class ResourceReference : EditorWindow
{
    // ...

    void OnGUI()
    {
        // ...

        GUILayout.Space(10);

        DrawNode();
    }

    // ...

    public static void DrawNode()
    {
        for (int i = 0; i < objects.Count; i++)
        {
            Object obj = objects[i];
            EditorGUILayout.ObjectField(obj, obj.GetType(), true);
        }
    }
}

现在窗口里就显示了被添加到 objects 列表中的预制体资源引用,点击它们还可以快速定位到 Assets 文件夹的资源。

绘制资源引用

定义链表节点

现在列表中只能展示预制体,如果要展示预制体中的某个节点或组件,为了让视图更加清晰地展示预制体与其节点的关联性,我们可以使用链表结构。

首先,定义链表节点,每个节点包含数据、类型、指针,并提供 Add 方法,在节点尾端链接下一个节点。

/// <summary>
/// 链表节点
/// </summary>
public class Node
{
    public Object data;   // 链表数据
    public string type;   // 链表类型
    public Node next;     // 链表指针

    public Node(Object data, string type)
    {
        this.data = data;
        this.type = type;
    }

    /// <summary>
    /// 链表尾插法
    /// </summary>
    /// <param name="data">链表数据</param>
    public Node Add(Object data, string type)
    {
        Node node = new Node(data, type);
        this.next = node;
        return node;
    }
}

绘制链式资源引用

修改一下 objects 列表的类型,改成 Node。

TraversePrefab 方法中,尝试去获取 SpriteRenderer 组件,如果存在,则生成一个 Node 节点,先把预制体 go 存入,并记录它的类型是 "prefab"

接着,还可以使用 Node 节点的 Add 方法,继续添加它的下一个节点,把 "SpriteRenderer""sprite" 都添加到节点链接中。

DrawNode 方法中,使用 do while 先绘制首个节点的内容(prefab),然后获取它的下一个节点,如果存在,就继续绘制,直到没有下一个节点为止。

// ...

public class ResourceReference : EditorWindow
{
    // ...
    public static List<Node> objects = new List<Node>();

    // ...

    public static void TraversePrefab()
    {
        var allfiles = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);

        foreach (var file in allfiles)
        {
            GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(file);
            if (go)
            {
                SpriteRenderer sr = go.GetComponent<SpriteRenderer>();
                if (sr)
                {
                    Node node = new Node(go, "prefab");
                    node.Add(sr, "SpriteRenderer").Add(sr.sprite, "sprite");
                    objects.Add(node);
                }
            }
        }
    }

    public static void DrawNode()
    {
        for (int i = 0; i < objects.Count; i++)
        {
            Node node = objects[i];
            do
            {
                EditorGUILayout.ObjectField(node.data, node.data.GetType(), true);
                node = node.next;
            } while (node != null);
        }
    }
}

现在重新点确定按钮,可以看到列表的内容发生了改变,筛选了包含 SpriteRenderer 组件的预制体,并且把它引用的图片也展示了出来,点击 Sprite 图片也可以快速定位到资源。

绘制链表节点

不过现在列表的内容是单列绘制的,改成三列才能很好地达到预期的效果。

只需要在 DrawNode 方法中,绘制每个 prefab 节点时,加上水平布局。

// ...

public class ResourceReference : EditorWindow
{
    // ...

    public static void DrawNode()
    {
        for (int i = 0; i < objects.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();

            Node node = objects[i];
            do
            {
                EditorGUILayout.ObjectField(node.data, node.data.GetType(), true);
                node = node.next;
            } while (node != null);

            EditorGUILayout.EndHorizontal();
        }
    }
}

现在列表就是三列的布局了。

三列布局

绘制滚动视图

目前列表里只有两行资源引用,如果项目里有很多资源的话,就需要使用滚动视图来展示。

先定义一个 Vector2 scrollPos 记录滚动视图当前的位置。

然后在 DrawNode 方法前后添加滚动视图代码。

// ...

public class ResourceReference : EditorWindow
{
    // ...

    Vector2 scrollPos = Vector2.zero;

    void OnGUI()
    {
        // ...

        scrollPos = EditorGUILayout.BeginScrollView(scrollPos);

        DrawNode();

        EditorGUILayout.EndScrollView();
    }
}

多复制几个预制体,视图就丰富起来了。

绘制滚动视图

简单应用:批量替换图片

绘制图片资源引用

首先,在窗口上绘制一个图片资源引用,可以手动选择要替换成哪张图片。

需要定义一个 Sprite replaceSprite 变量,在 OnFocus 方法中,加载一张默认的图片进行赋值。

OnGUI 方法中,判断 replaceSprite 是否为空,然后再进行绘制,末尾加上 as Sprite 把 Object 类型转换成 Sprite。

注意:如果传入 EditorGUILayout.ObjectField 的对象未赋值,窗口绘制时会报错,在 OnFocus 方法中进行赋值,每次聚焦到窗口时就能保证 replaceSprite 不为空。

// ...

public class ResourceReference : EditorWindow
{
    // ...

    public static Sprite replaceSprite;
    
    void OnFocus()
    {
        if (replaceSprite == null)
        {
            replaceSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Textures/Default.png");
        }
    }

    void OnGUI()
    {
        // ...

        GUILayout.Space(10);

        if (replaceSprite != null)
        {
            replaceSprite = EditorGUILayout.ObjectField("替换图片", replaceSprite,
                replaceSprite.GetType(), true) as Sprite;
        }

        GUILayout.Space(10);

        // ...
    }
}

现在窗口里就出现了一张默认的替换图片,点击图片还可以打开图片选择窗口,自由选择其他图片。

绘制替换图片

替换图片

首先,绘制一个替换按钮,把图片资源引用和替换按钮放在一个水平布局中。

替换按钮调用 ReplaceSprite 方法。

// ...

public class ResourceReference : EditorWindow
{
    // ...

    void OnGUI()
    {
        // ...

        GUILayout.Space(10);

        EditorGUILayout.BeginHorizontal();

        if (replaceSprite != null)
        {
            replaceSprite = EditorGUILayout.ObjectField("替换图片", replaceSprite,
                replaceSprite.GetType(), true) as Sprite;
        }

        GUILayout.Space(50);

        if (GUILayout.Button("替换", GUILayout.Height(50)))
        {
            ReplaceSprite();
        }

        EditorGUILayout.EndHorizontal();

        GUILayout.Space(10);

        // ...
    }
}

接着实现 ReplaceSprite 方法。

可以先判断一下 replaceSpriteobjects 列表是否为空,如果为空,就弹窗提示。

然后遍历 objects 列表,先取出 prefab 数据,在 do while 中查找 "SpriteRenderer" 类型的数据,修改它的 sprite 属性,同时修改 "sprite" 类型的数据,让窗口列表的第三列数据能够刷新。

完成图片替换后,使用 PrefabUtility.SavePrefabAsset 保存预先取出的 prefab 数据,让 Assets 文件夹中的预制体资源同步修改,然后使用 AssetDatabase.Refresh 刷新 Assets 文件夹。

最后可以给一个弹窗提示,表示已经完成了图片的替换。

// ...

public class ResourceReference : EditorWindow
{
    // ...

    public static void ReplaceSprite()
    {
        if (replaceSprite == null)
        {
            EditorUtility.DisplayDialog("温馨提示", "替换图片为空", "okk");
            return;
        }
        
        if (objects.Count == 0)
        {
            EditorUtility.DisplayDialog("温馨提示", "节点列表为空", "okk");
            return;
        }

        for (int i = 0; i < objects.Count; i++)
        {
            Node node = objects[i];
            GameObject prefab = node.data as GameObject;
            do
            {
                if (node.type == "SpriteRenderer")
                {
                    SpriteRenderer sr = node.data as SpriteRenderer;
                    sr.sprite = replaceSprite;
                }
                else if (node.type == "sprite")
                {
                    node.data = replaceSprite;
                }
                node = node.next;
            } while (node != null);

            PrefabUtility.SavePrefabAsset(prefab);
        }

        AssetDatabase.Refresh();

        EditorUtility.DisplayDialog("温馨提示", "替换图片成功", "okk");
    }
}

替换图片演示:

替换图片演示

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

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

相关文章

【函数模板】参数类型

一、默认参数 1.默认参数的调用 函数模板的参数类型可以指定一个默认值&#xff0c;在不传入参数类型的时候将使用默认参数类型来实例化函数模板。 例如&#xff1a; template<typename T, typename R int> auto add(T a, R b 0) -> decltype(a b) {std::cout &…

HTML5CSS3--CSS3的各种用法

1.background-origin 背景图起点&#xff1a; padding-box背景图像相对于内边距框来定位。border-box背景图像相对于边框盒来定位。content-box背景图像相对于内容框来定位。 2.background-clip 背景图裁剪&#xff1a; border-box默认值。背景绘制在边框方框内&#xff0…

AcWing 282. 石子合并

必看的视频讲解↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 【E28【模板】区间DP 石子合并——信息学竞赛算法】 合并过程总开销等于红色数字总和&#xff0c;可以理解为花费的总体力&#xff01; f数组的含义是f【i】【j】是从第i堆石子开始到第…

HighPoint发布NVMe RAID外壳以应对严苛环境

2024年8月23日&#xff0c;HighPoint揭开了其新款RocketStor 654x系列外部NVMe RAID外壳的神秘面纱。这款新产品旨在将第四代存储应用提升至全新水平&#xff0c;为工业、边缘计算平台以及专业工作站环境提供强大的存储解决方案。 #### 技术亮点 - **高性能与大容量** Rocke…

【服务器篇】买服务器想安装宝塔面板,看这一篇够了

服务器选择&#xff1a;CentOS Stream-9 服务器厂商&#xff1a;腾讯云&#xff08;没有收钱&#x1f4b4;&#xff09; 使用工具&#xff1a;宝塔&#xff08;没收钱&#xff09; 建议两位大佬给俺冲冲钱&#xff0c;嘻嘻&#x1f92d; 系列文章目录 提示&#xff1a;这里可以…

VMware Workstation安装及配置CentOS7 Linux操作系统

VMware workstation安装 百度网盘&#xff0c;VMware-workstation-full-17.5.2.exe 安装包&#xff1a; 链接:https://pan.baidu.com/s/1xgbWUlo-hFUbb11MRxIVsw?pwd87bq 提取码: 87bq 检查网络适配器是否正常配置 在VMware Workstation中安装CentOS7 Linux操作系统 下载…

财务上的弹性

财务管理和时间管理&#xff0c;是有一定关联的。 一般来说&#xff0c;财务上的弹性&#xff0c;会带来时间管理的弹性。财务上的紧张&#xff0c;会带来时间管理的紧张。 比如&#xff0c;一个人财务状况很好&#xff0c;就可以用更多的时间挑选合适的工作。在工作期间&…

【LangChain】使用LangChain的提示词模板:技巧与总结

&#x1f601; 作者简介&#xff1a;前端开发爱好者&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript小贴士 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继续…

【困难】 猿人学web第一届 第15题 备周则意怠,常见则不疑

数据接口分析 数据接口 https://match.yuanrenxue.cn/api/match/15 请求时需要携带 page 页码&#xff0c;m为加密参数 cookie中没有加密信息&#xff0c;携带 SessionId请求即可 加密参数还原 查看数据接口对应的 requests 栈 m参数 是通过 window.m() 方法执行后得到的 打上…

【免费分享】2024最新优化算法-黑翅鸢算法BKA

黑翅鸢优化算法&#xff08;Black-winged kite algorithm&#xff0c;BKA&#xff09;是一种受自然界启发的群体智能优化算法&#xff0c;其设计灵感源自黑翅鸢&#xff08;Black-winged kite&#xff09;的生存策略。黑翅鸢在攻击和迁徙过程中展现出的高度适应性和智能行为&am…

3分钟带你手把手安装一款音乐制作神器——FL Studio 24.1.1.4285中文版

大家好&#xff0c;今天我要给大家介绍一款音乐制作神器——FL Studio 24.1.1.4285中文版。这款软件可是音乐制作界的翘楚&#xff0c;无论是专业人士还是音乐爱好者&#xff0c;都会为它的强大功能和易用性所折服。 我们来看看FL Studio的特点。这是一款全能型的音乐工作站&am…

ACL实验配置学习笔记

拓扑描述&#xff1a; R1作为所有PC的网关&#xff1b; 财务部用户&#xff1a;192.168.1.0/24 市场部用户&#xff1a;192.168.2.0/24 Server1&#xff1a;HTTP服务器地址为7.7.7.7/24 PC 2&#xff1a;192.168.1.2 PC 5:&#xff1a;192.168.2.2 PC 3&#xff1a;&…

干货分享|分享一款高效的软件卸载神器 Geek Uninstaller

问题&#xff1a;卸载软件时&#xff0c;时常会留下残留文件和注册表。当遇到流氓软件&#xff0c;还常常卸载失败。 1.软件介绍 特点&#xff1a;高效快速&#xff0c;小巧便携。100% 免费 2.下载方法 官方下载网站&#xff1a;Geek Uninstaller - the best FREE uninstaller …

《深入理解JAVA虚拟机(第2版)》- 第6章 - 学习笔记

第6章 类文件结构 6.1 概述 字节码和二级制本地机器码&#xff08;Native Code&#xff09;是用来存储程序编译后的结果的&#xff0c;是二种程序存储结构。 6.2 无关性的基石 这里说的无关性&#xff0c;分为&#xff1a;平台无关性和语言无关性。 平台无关性&#xff1a;…

Codeforces Round 913 (Div. 3) D. Jumping Through Segments (二分*1400)

很容易看出这道题应该二分答案&#xff0c;本题的难点在于对于mid的验证。 找距离肯定是不难&#xff0c;难就难在我们输入的区间并不是按照左右顺序排列的&#xff0c;有的区间可能涵盖住了另一个区间&#xff0c;也就是说在这里我们需要进行的是左右的移动。 那么我们根本无…

VBA数据库解决方案第十四讲:如何在数据库中动态删除和建立数据表

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

【楼兰图腾】

题目 思路 本质上这个问题就是在求分别在一个数左边和右边的&#xff0c;大于该数的个数的乘积&#xff08;小于同理&#xff09; 维护一个下标指元素大小的线段树来方便求大于和小于某数值的元素个数 通过从左到右遍历&#xff0c;来确定此时的线段树状态一定不包括右边 因为…

【C++ Primer Plus习题】8.3

问题: 解答: #include <iostream> #include <string> #include <cctype> using namespace std;void function(string& str) {for (int i 0; i < str.size(); i){str[i]toupper(str[i]);} }int main() {string str;while (true){cout << "…

【超详细】深度学习的Hello World:使用pytroch训练一个自定义的手写体数字识别模型完整流程【附数据集与完整源码】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发…

Unet改进15:添加TripletAttention||减少冗余计算和同时存储访问

本文内容:在不同位置添加TripletAttention注意力机制 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 由于注意机制具有在通道或空间位置之间建立相互依赖关系的能力,近年来在各种计算机视觉任务中得到了广泛的研究和应用。在本文中,我们研究了轻量级但…