Unity 编辑器-查找所有未被使用的Prefab

news2024/11/15 15:35:13
需求

接到一个需求,将Res里所有特效相关的prefab检查一下,没有使用的移除。

分析

先拆解一下需求,如下

需求
检测筛选
移除
和其他Prefab间的直接引用
代码或配表引用
代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Object = UnityEngine.Object;

public class SearchUseing :EditorWindow
{
    
    private Vector2 _pos = Vector2.zero;
    static string[] fileExtensions = { ".cs", ".js", ".txt" };//你查询的文件格式 如 .json 等
    private static List<Object> _objs = new List<Object>();
    private static Dictionary<string, List<string>>  _referenceCache = new Dictionary<string, List<string>>();
    private static Dictionary<string, Object> _allUnUsedDic = new Dictionary<string, Object>();

    private static Dictionary<string, List<string>> _resultDic = new Dictionary<string, List<string>>();
    private  static void ShowWindow()
    {
        var window = (SearchUseing)EditorWindow.GetWindow(typeof(SearchUseing));
        window.titleContent = new GUIContent("Object List");
        window.Show();
    }

    private void OnGUI()
    {
        // 在 EditorWindow 中使用 GUILayout 绘制 UI 元素
        GUILayout.BeginHorizontal();
        GUILayout.Label("选中的Asset列表", EditorStyles.boldLabel);

        if (GUILayout.Button("导出空引用的文件路径"))
        {
            string selectedFolderPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            string folderName = System.IO.Path.GetFileName(selectedFolderPath);
            Output(_resultDic,folderName,true);
        }
        if (GUILayout.Button("导出所有文件及依赖路径"))
        {
            string selectedFolderPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            string folderName = System.IO.Path.GetFileName(selectedFolderPath);
            Output(_resultDic,folderName,false);
        }
        if (GUILayout.Button("移动空引用prefab"))
        {
            MoveAll();
        }
        GUILayout.EndHorizontal();
        Rect rect = new Rect(10, 20, position.width - 20, position.height - 30);
        GUILayout.BeginArea(rect);
        if (_allUnUsedDic is { Count: > 0 })
        {
            _pos = GUILayout.BeginScrollView(_pos);
            foreach (var kv in _resultDic)
            {
                EditorGUILayout.ObjectField(AssetDatabase.LoadMainAssetAtPath(kv.Key), typeof(Object), false);
                foreach (var s in kv.Value)
                {
                    EditorGUILayout.ObjectField(AssetDatabase.LoadMainAssetAtPath(s), typeof(Object), false);
                }
                GUILayout.Space(20);
            }

            GUILayout.EndScrollView();
        }
        GUILayout.EndArea();
        
    }
    [MenuItem("Assets/工具/查找空引用的Prefab")]
    public static void SearchAll()
    {
        _resultDic = new Dictionary<string, List<string>>();
        GetAllDependency();
        foreach (var folderPath in Selection.GetFiltered(typeof(DefaultAsset), SelectionMode.Assets))
        {
            var assetPath = AssetDatabase.GetAssetPath(folderPath);
            var assets = AssetDatabase.FindAssets("t:Prefab", new[] { assetPath });
            foreach (var asset in assets)
            {
                var assetFilePath = AssetDatabase.GUIDToAssetPath(asset);
                _resultDic.Add(assetFilePath,new List<string>());
                ReferenceFilter(assetFilePath);
                _allUnUsedDic.TryAdd(assetFilePath,AssetDatabase.LoadMainAssetAtPath(assetFilePath));
            }
        }

        SearchInAssets(_allUnUsedDic);
        if (_resultDic is { Count: > 0 })
        {
            ShowWindow();
        }
        
    }
    //导出显示结果的txt文件
    private static void Output(Dictionary<string,List<string>> dic,string fileName,bool isNull)
    {
        string filePath = $"temporaryCacheRes/Dic_{fileName}{(isNull ? "1" : "2")}.txt"; // 文件保存路径,可以自己指定

        using (StreamWriter streamWriter = new StreamWriter(filePath))
        {
            foreach (KeyValuePair<string, List<string>> kvp in dic)
            {
                if (isNull&&kvp.Value.Count == 0 || !isNull)
                {
                    streamWriter.WriteLine(kvp.Key); // 写入Key值

                    List<string> items = kvp.Value; 
                    foreach (string item in items)
                    {
                        streamWriter.WriteLine("\t" + item); // 写入Value值,使用tab键缩进
                    }
                    streamWriter.WriteLine("\n" ); 
                }
            }
            AssetDatabase.Refresh();
        }
    }
    private static void GetAllDependency()
    {
        _referenceCache = new Dictionary<string, List<string>>();
        _allUnUsedDic = new Dictionary<string, Object>();
        _objs = new List<Object>();
        var guids = AssetDatabase.FindAssets("");
        foreach (var guid in guids)
        {
            var assetPath = AssetDatabase.GUIDToAssetPath(guid);
            var dependencies = AssetDatabase.GetDependencies(assetPath, false);

            foreach (var dependency in dependencies)
            {
                if (_referenceCache.ContainsKey(dependency))
                {
                    if (!_referenceCache[dependency].Contains(assetPath))
                    {
                        _referenceCache[dependency].Add(assetPath);
                    }
                }
                else
                {
                    _referenceCache[dependency] = new List<string>() { assetPath };
                }
            }
        }
    }
    private static void ReferenceFilter(string path)
    {
        if (_referenceCache.ContainsKey(path))
        {
            foreach (var reference in _referenceCache[path])
            {
                Debug.Log(reference, AssetDatabase.LoadMainAssetAtPath(reference));
                var prefabObj = AssetDatabase.LoadAssetAtPath<Object>(reference);
                if (!_objs.Contains(prefabObj))
                {
                    _objs.Add(prefabObj);
                }
            }

            _resultDic[path] = _referenceCache[path];
        }
        else
        {
            _allUnUsedDic.TryAdd(path,AssetDatabase.LoadMainAssetAtPath(path));
            Debug.LogWarning($"{path}  没有直接引用");
        }

    }
    
    private static void MoveAll()
    {
        foreach (var kv in _allUnUsedDic)
        {
            Debug.Log(kv.Value.name);
            MovePrefabToFolder(kv.Key, kv.Value.name);
            _resultDic.Remove(kv.Key);
        }
        AssetDatabase.Refresh();
    }
    
    //移动到指定路径
    static void MovePrefabToFolder(string path,string prefabName)
    {
        string targetFolderPath = "Assets/Art/Effect/Temp/";
        CreateFolderIfNotExists("Assets/Art/Effect","Temp");
        if (!AssetDatabase.IsValidFolder(targetFolderPath))
        {
            Debug.LogWarning("Invalid folder path: " + targetFolderPath);
            return;
        }

        // 组装新的Prefab路径并移动到指定文件夹
       
        string newPrefabPath = targetFolderPath + prefabName + ".prefab";
        AssetDatabase.MoveAsset(path, newPrefabPath);
        AssetDatabase.SaveAssets();

        Debug.Log("Prefab " + prefabName+ " moved to " + newPrefabPath);
    }
    // public static void SerachInAssets()
    // {
    //     var path = AssetDatabase.GetAssetPath(Selection.activeObject);
    //     var obj = AssetDatabase.LoadMainAssetAtPath(path);
    //     Debug.Log(obj.name);
    //     // SearchInAssets(obj.name,path);
    //     // SearchInTxtFiles(obj.name);
    // }
    //在Assets文件夹中进行搜索
   static void SearchInAssets(Dictionary<string,Object> dic)
    {
        string[] paths = AssetDatabase.GetAllAssetPaths();

        foreach (string path in paths)
        {
            // if (!path.StartsWith("Assets/Game") && !path.StartsWith("Assets/Res/Table/effect"))
            // {
            //     continue;
            // }
            
            if (!path.StartsWith("Assets/Res/Table/effect"))  //写你的筛选条件,比如我这里项目中不会直接使用字符串加载,只会在指定路径下查配表文件
            {
                continue;
            }
            // if (path.StartsWith("Assets/StreamingAssets")||path.StartsWith("Assets/Temp"))
            // {
            //     continue;
            // }

            
            if (AssetDatabase.IsValidFolder(path))
            {
                continue;
            }

            if (IgnoreFile(Path.GetFileName(path)))
            {
                continue;
            }

            if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(GameObject))
            {
                //如果是prefab 则不检测(ReferenceFilter已经检测过prefab)
                continue;
            }
            string text = File.ReadAllText(path);
            List<string> tempRemoveList = new List<string>(); 
            foreach (var kv in dic)
            {
                if (_resultDic.ContainsKey(kv.Key))
                {
                    var tempList = _resultDic[kv.Key] ??= new List<string>();
                    var name = kv.Value.name;
                    if (text.Contains(name) && path!=kv.Key )
                    {
                        Debug.Log("Found text in asset: " + path );
                        tempList.Add(path);
                        _resultDic[kv.Key] = tempList;
                        tempRemoveList.Add(kv.Key);
                    }
                }
                else
                {
                    Debug.Log($"当前遍历的路径不在result内,查查为啥");
                }
                
               
            }
            foreach (var s in tempRemoveList)
            {
                _allUnUsedDic.Remove(s);
            }
        }
        Debug.Log("searchend");
    }
 
    //在txt文件中进行搜索
    static void SearchInTxtFiles(string searchText)
    {
        DirectoryInfo directory = new DirectoryInfo(Application.dataPath);

        FileInfo[] files = directory.GetFiles("*.*", SearchOption.AllDirectories).Where(
            f => fileExtensions.Contains(f.Extension.ToLower())).ToArray();

        foreach (FileInfo file in files)
        {
            string text = File.ReadAllText(file.FullName);

            if (text.Contains(searchText))
            {
                Debug.Log("Found text in txt file: " + file.FullName);
            }
        }

        Debug.Log("searchend");
    }

    static bool IgnoreFile(string fileName)
    {
        return fileName == "TextSearchEditor.cs";
    }
    private void OnDestroy()
    {
        _referenceCache.Clear();    
    }
    static void CreateFolderIfNotExists(string path,string name)
    {
        // 检查文件夹是否存在
        if (!AssetDatabase.IsValidFolder(path+"/"+name))
        {
            // 创建文件夹
            AssetDatabase.CreateFolder(path, name);
            AssetDatabase.Refresh();
            Debug.Log("Folder Create at " + path+"/"+name);
        }
        else
        {
            Debug.Log("Folder already exists at " + path);
        }
    }
}

结果

结果如图所示。每组元素第一个为查找的prefab,之后的是使用了该prefab的预设或配表。
如果一组元素只有一个对象,则这个prefab无引用。
顶部的按钮为指定功能。
PS:使用Prefab名字做的检测,未对同名Prefab做筛选判断

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

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

相关文章

docke安装elasticsearch(ES)

docke安装elasticsearch&#xff08;ES&#xff09; 1.安装一个不带数据卷映射的ES docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" elasticsearch:7.6.22.创建es数据卷映射目录 mkdir -p data/elasticsearch3.将…

瑞芯微 RK356x 基于Android11移植usb接口rtl8723du wifi和蓝牙一体化

开发环境 平台: 瑞芯微RK356x 操作系统&#xff1a;Android11 WiFi、蓝牙芯片:RTL8723DU 通讯类型&#xff1a;USB协议 RTL8723du介绍 Realtek RTL8723DU是一个高度集成的单片机802.11b/g/n 1T1R WLAN&#xff0c;和一个集成的蓝牙2.1/4.2单片机&#xff0c;USB 2.0多功能。…

谈谈电机的FOC控制算法的特点以及应用场景

电机的FOC&#xff08;Field-Oriented Control&#xff09;控制算法是一种常用的电机控制策略。它的特点是将电机的控制分为两个部分&#xff1a;电流控制和转速控制。 首先&#xff0c;电流控制是FOC算法的关键部分。它通过控制电机的电流来实现对电机的力矩控制。具体来说&am…

maven引入jar包报红

maven引入jar包报红 1、检查自己的maven配置有无问题 2、检查是否没有子项目使用到当前引入的jar包&#xff0c;在根目录下引入的jar包如果没有子项目使用会报红&#xff0c;原因是在根目录下只是声明式引用&#xff0c;并没有实际引用到。 解决办法&#xff1a;找到要使用的子…

Leetcode-每日一题【1721.交换链表中的节点】

题目 给你链表的头节点 head 和一个整数 k 。 交换 链表正数第 k 个节点和倒数第 k 个节点的值后&#xff0c;返回链表的头节点&#xff08;链表 从 1 开始索引&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2输出&#xff1a;[1,4,3,2,5] 示例 …

816. 数组翻转

链接&#xff1a; 链接 题目&#xff1a; 给定一个长度为 nn 的数组 aa 和一个整数 sizesize&#xff0c;请你编写一个函数&#xff0c;void reverse(int a[], int size)&#xff0c;实现将数组 aa 中的前 sizesize 个数翻转。 输出翻转后的数组 aa。 输入格式 第一行包含两个整…

高德地图通过图层layer实现对海量点的可视化渲染

一、可视化海量点应用场景 在正文开始之前我先说说我为啥会使用这个技术来实现数据的可视化。 事情是这样的&#xff0c;我接手了一个项目&#xff0c;里面有个需求是在地图上标记出他们公司的产品的使用分布。我接手的时候呢&#xff0c;我前面的那位大哥是使用marker点覆盖物…

Spark(19):SparkSQL中数据的加载和保存

目录 0. 相关文章链接 1. 通用的加载和保存方式 1.1. 加载数据 1.2. 保存数据 2. Parquet 2.1. 加载数据 2.2. 保存数据 3. JSON 4. CSV 5. MySQL 5.1. 导入依赖 5.2. 读取数据 5.3. 写入数据 6. Hive 6.1. SparkSQL连接Hive 6.2. 内嵌的 HIVE 6.3. 外部的 HI…

FastDFS文件系统

FastDFS文件系统 环境代码实现 一.FastDFS 1.什么是FastDFS FastDFS:Fast Distributed File System,快速的分布式文件系统,是一款用c语言开发的开源的分布式文件系统. FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制&#xff0c;充分考虑了…

前端漏洞xss

网络钓鱼、获取Cookie、强制弹窗获取流量、网站挂马(将恶意代码嵌入程序&#xff0c;用户浏览页面时计算机将被嵌入木马)、发送垃圾信息或广告、传播蠕虫病毒 漏洞原理 XSS(Cross Site Scripting),是一种跨站的脚本攻击&#xff0c;曾简称为CSS&#xff0c; 后改为XSS。 攻击…

springboot在线考试

在线考试系统的开发运用java技术&#xff0c;MIS的总体思想&#xff0c;以及MYSQL等技术的支持下共同完成了该系统的开发&#xff0c;实现了在线考试管理的信息化&#xff0c;使用户体验到更优秀的在线考试管理&#xff0c;管理员管理操作将更加方便&#xff0c;实现目标.

Dell服务器的iDRAC管理卡连接

Dell服务器的iDRAC管理卡图文教程 1、网线连接idrac口2、查看idrac地址3、匹配IP地址4、web登录idrac页面5、登录成功页面 带有集成戴尔远程访问控制器 &#xff08;idrac&#xff09;的系统具有默认用户名和密码&#xff0c;但您也可以使用安全密码对其进行配置。默认使用web浏…

在idea中添加mapper.xml文件模板

文章目录 1、创建模板2. 创建mapper.xml文件 1、创建模板 打开创建模板页面 位置&#xff1a;File-》Settings-》Editor-》File and Code Templates 点击号&#xff0c;新建模板&#xff0c;填入Name&#xff08;随意&#xff09;、Extension&#xff08;必须为xml&#xff…

hadoop安装集群搭建

准备工作&#xff1a; 1&#xff0c;创建一台虚拟机&#xff0c;安装java jdk 这里选择1.8 2&#xff0c;给虚拟机并配置好静态IP地址 一&#xff1a;Java jdk安装 1.1检查安装环境&#xff0c;是否已安装其他版本的jdk&#xff0c;有的话卸载。 # 查看系统JDK进程 [rooth…

PSINS工具箱学习(二)姿态阵、四元数、欧拉角、等效旋转矢量的概念和转换

上一篇博客&#xff1a;PSINS工具箱学习&#xff08;一&#xff09;下载安装初始化、SINS-GPS组合导航仿真、习惯约定与常用变量符号、数据导入转换、绘图显示 文章目录 一、基础概念1、坐标系定义1. 惯性坐标系&#xff08; i 系 &#xff09;2. 地心地固坐标系&#xff08; e…

solr快速上手:搭建solr集群并创建核心,设置数据同步(十一)

0. 引言 前几章我们已经讲解了solr单机版的基本使用&#xff0c;但实际生产中&#xff0c;为了保证高可用、高性能&#xff0c;我们一般会采用集群模式&#xff0c;所以接下来&#xff0c;我们继续讲解solr集群的搭建和基本操作 1. 集群模式 1.1 分片 在讲解solr集群模式前…

【七天入门数据库】第二天 数据库理论基础

系列文章传送门&#xff1a; 【七天入门数据库】第一天 MySQL的安装部署 【七天入门数据库】第二天 数据库理论基础 【七天入门数据库】第三天 MySQL的库表操作 文章目录 一、什么是数据库 二、数据库管理系统DBMS 三、数据库与文件系统的区分 四、数据库的发展史 五、…

选择屏幕文本元素

标题 列标题 当你使用write语句时才能展现使用. 效果: 选择屏幕文本符号

前端Vue自定义tabs标题栏选项卡组件 可设置文字color

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

7.10 qt作业

闹钟 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QIcon> #include <QLabel> #include <QTextEdit> #include <QPushButton> #include <QLineEdit> #include <QPainter> #include <…