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

需求

接到一个需求,将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.hqwc.cn/news/21943.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【运维】 第02讲(上):企业 Nginx 高性能优化配置实战总结

本课时讲解关于 Nginx 配置优化的内容&#xff0c;相信对于 Nginx 你一定并不陌生&#xff0c;它是一款轻量级的开源 Web 服务及代理程序。在 Nginx 出现之前市场上主流两款 Web 服务&#xff0c;一款是 Windows 系统上的 IIS&#xff0c;另外一款是 Linux 系统上的 Apache。而…

新耀东方|安全狗亮相2023第二届上海网络安全博览会

7月5日至7日&#xff0c;“新耀东方-2023第二届上海网络安全博览会暨高峰论坛”在上海顺利举办。此次大会由上海市信息网络安全管理协会、国家计算机网络应急技术处理协调中心上海分中心、(ISC)2上海分会、上海市普陀区科学技术委员会、上海市网络安全产业示范园共同主办。 作为…

【前后缀GCD】ABC125 C

C - GCD on Blackboard (atcoder.jp) 题意&#xff1a; 思路&#xff1a; 这道题&#xff0c;多看看样例大概就出来了 由样例可知&#xff0c;换成的数可以是其他N-1个数的gcd&#xff0c;这样对答案的贡献也没有影响 所以直接去枚举换的是什么数就行 因此怎么去快速求其他…

webpack5性能优化

webpack构建速度 一、优化babel-loader 注意&#xff1a;开启缓存,配置后打包是就能缓存babel webpack.common.js文件命中缓存cacheDirectory {test: /\.js$/,use: [babel-loader?cacheDirectory],include: srcPath,exclude: /node_modules/ }, 测试&#xff1a; 打包后的…

开源堡垒机Guacamole二次开发记录之一

简介 项目中需要用到堡垒机功能&#xff0c;调研了一大圈&#xff0c;发现了Apache Guacamole这个开源项目。 Apache Guacamole 是一个无客户端的远程桌面网关&#xff0c;它支持众多标准管理协议&#xff0c;例如 VNC(RFB)&#xff0c;RDP&#xff0c;SSH 等等。该项目是Apa…

DuiLib中的list控件以及ListContainerElement控件

文章目录 前言1、创建list控件2、创建 ListContainerElement 元素&#xff0c;并添加到 List 控件中,这里的ListContainerElement用xml来表示3、在 ListContainerElement 元素中添加子控件 1、List控件2、ListContainerElement控件 前言 在 Duilib 中&#xff0c;List 控件用于…

【力扣算法12】之 11. 盛最多水的容器 python

文章目录 问题描述示例1示例2提示 思路分析代码分析完整代码详细分析运行效果截图调用示例运行结果完结 问题描述 给定一个长度为 n 的整数数组 height 。有n条垂线&#xff0c;第i条线的两个端点是(i, 0)和(i, height[i])。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构…

Swagger、knife4j简介

Swagger 简介 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(API Documentation & Design Tools for Teams | Swagger)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接口的…

网络安全与防范

1.重要性 随着互联网的发达&#xff0c;各种WEB应用也变得越来越复杂&#xff0c;满足了用户的各种需求&#xff0c;但是随之而来的就是各种网络安全的问题。了解常见的前端攻击形式和保护我们的网站不受攻击是我们每个优秀fronter必备的技能。 2.分类 XSS攻击CSRF攻击网络劫…

【三维重建】【深度学习】NeuS总览

【三维重建】【深度学习】NeuS总览 论文提出了一种新颖的神经表面重建方法&#xff0c;称为NeuS&#xff0c;用于从2D图像输入以高保真度重建对象和场景。在NeuS中建议将曲面表示为有符号距离函数(SDF)的零级集&#xff0c;并开发一种新的体绘制方法来训练神经SDF表示&#xff…

亚马逊云科技联合Nolibox定制工业设计AIGC解决方案

从机器学习算法到深度学习再到强化学习&#xff0c;AI创新浪潮奔流不息。而AIGC&#xff08;AI-generated Content&#xff0c;人工智能生成内容&#xff09;的到来&#xff0c;更是让AI成为众多企业的得力助手&#xff0c;开拓了文本、图像、音视频等领域的天花板。 在洞悉到…

简爱思维导图怎么画?几个超实用绘制步骤赶紧get

简爱思维导图怎么画&#xff1f;思维导图是一种有效的信息组织和表达工具&#xff0c;能够帮助我们更好地整理思路、提高学习效率。下面这篇文章就带大家了解一下简爱思维导图的绘制步骤&#xff0c;并分享4个超实用步骤&#xff0c;助你快速掌握。 在绘制思维导图之前&#xf…