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.mzph.cn/news/284.shtml

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

相关文章

android editText获取不到数据

问题分析&#xff1a;在onActivityCreated一开始就创建了findViewById&#xff0c;这时获取的是默认值&#xff0c;需要在点击按钮时重新加载才能获取到输入数据。 需要在点击按钮时重新加载数据&#xff1a;

大坝安全监测中需要做好检查监测

大坝安全监测是人们了解大坝运行状态和安全状况的有效手段和方法。它的目的主要是了解大坝安全状况及其发展态势&#xff0c;是一个包括由获取各种环境、水文、结构、安全信息到经过识别、计算、判断等步骤&#xff0c;最终给出一个大坝安全 程度的全过程。 此过程包括&#xf…

初识stm32

1、什么是单片机&#xff1f; 单片机&#xff08;Single-Chip Microcomputer&#xff09;是一种集成电路芯片&#xff0c;把具有数据处理能力的中央处 理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功 能&#xff08;可能还包括显示驱动电路、…

华为云流水线CodeArts Pipeline怎么样?能实现哪些功能?

华为云流水线服务CodeArts Pipeline&#xff0c;旨在提升编排体验&#xff0c;开放插件平台&#xff0c;并提供标准化的DevOps企业治理模型&#xff0c;将华为公司内的优秀研发实践赋能给伙伴和客户。 灵活编排、高效调度 开放流水线插件 内置企业DevOps研发治理模型 体验通…

【工业机器人】用于轨迹规划和执行器分析的机械手和移动机器人模型(MatlabSimulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

C. Strong Password

Problem - C - Codeforces 思路&#xff1a;根据题意我们能够知道就是对于每一位都要再区间范围内&#xff0c;并且不是s的子序列&#xff0c;我们先看第一位&#xff0c;第一位有l[1]-r[1]这几种选择&#xff0c;假如说某一种选择在s中没有那么我们就选择以这个开头的作为答案…

python_股票增加控制人与流通股东等筛选条件

目录 写字前面&#xff1a; 结果展示 获取数据 行业数据 控制人数据 十大流通股东数据 开始合并 1 从行业数据中提取证券股的行业数据 2 合并控制人数据 3 合并十大流通股东 4 把三个结果按列合并 写字前面&#xff1a; 在分析数据的时候&#xff0c;常常需要的字段…

如何实现CAN-SOME/IP通信路由测试

区别于基于UDP的车内通信路由&#xff0c;基于SOME/IP协议的路由增加了服务发现&#xff08;Service Discovery&#xff09;和服务发布&#xff08;Service Publish&#xff09;&#xff0c;那对于测试工程师来说&#xff0c;怎么实现CAN-SOME/IP路由的测试呢&#xff1f; 01 …

【K8S系列】深入解析K8S调度

序言 做一件事并不难&#xff0c;难的是在于坚持。坚持一下也不难&#xff0c;难的是坚持到底。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记论点蓝色&#xff1a;用来标记论点 Kubernetes (k8s) 是一个容器编…

基于springboot的智慧养老系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

数学建模学习之简单设备分配问题

简单的设备分配问题 某公司新购置了某种设备 6台&#xff0c;欲分配给下属的4 个企业&#xff0c;已知各企业获得这种设备后年创利润如表 1.1 所示&#xff0c;单位为千万元。问应如何分配这些设备能使年创总利润最大&#xff0c;最大利润是多少? 表1.1的数据为&#xff1a; 对…

Go 微服务开发框架 DMicro 的设计思路

Go 微服务开发框架 DMicro 的设计思路 DMicro 源码地址: Gitee:dmicro: dmicro是一个高效、可扩展且简单易用的微服务框架。包含drpc,dserver等 背景 DMicro 诞生的背景&#xff0c;是因为我写了 10 来年的 PHP&#xff0c;想在公司内部推广 Go, 公司内部的组件及 rpc 协议…

LeetCode 142.环形链表II

142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *detectCycle(ListNode …

4.日志分布式-ELK

文章目录 日志分布式-ELK概念可以添加的其它组件filebeat 结合 logstash 带来好处为什么要使用 ELK缓存和Fluentd完整日志系统基本特征ELK 的工作原理 部署Elasticsearchjdk环境和防火墙配置安装Elasticsearch修改配置文件优化内存参数启动程序并测试效果安装 Elasticsearch-he…

leetcode-344. 反转字符串

leetcode-344. 反转字符串 文章目录 leetcode-344. 反转字符串一.题目描述二.第1次提交(std::reverse)三.第2次提交(左右指针)四.第3次提交(左右指针&#xff0c;swap函数)五.第4次提交(左右指针) 一.题目描述 二.第1次提交(std::reverse) class Solution {public:void revers…

Springboot启用HTTP响应压缩

官方文档:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/htmlsingle/#how-to-enable-http-response-compression

mmc记录

1、获取csd&#xff0c;也就是DSR寄存器 说是应该可以获取块长度、卡存储容量等&#xff0c;但是在8953上&#xff0c;没看到这个日志 参考&#xff1a; 一、有6个主要的和寄存器 1、OCR寄存器 描述了存储卡的Vdd电压描述 &#xff0c;总共32Bit Bit31 --- 卡上电状态位&…

赋能智能智造-RK3568智能主板助力机器人产业高速发展

机器人作为现代制造业的重要一环&#xff0c;正在以惊人的速度推动着生产效率和智能化水平的提升&#xff0c;它们在生产线上的准确操作和高效工作&#xff0c;为企业带来了巨大的竞争优势。关于工业机器人的编程和控制技术&#xff0c;在过去几年中已经有了很多发展和新的应用…

计算机网络——物理层

物理层 物理层是计算机网络体系结构中的底层层级&#xff0c;负责处理计算机与物理传输媒介之间的接口和通信细节。它主要关注如何在物理媒介上传输原始比特流&#xff0c;并确保数据能够可靠地从发送方传输到接收方。 物理层的主要任务包括&#xff1a; 传输介质&#xff1a…

谷歌Bard入门指南

文章目录 谷歌Bard入门指南一、简介二、使用指南三、中文化3.1 中文提问3.2 中文回答 四、Hello Game五、亮点 谷歌Bard入门指南 一、简介 Bard 是一个大型语言模型&#xff0c;也称为对话式 AI 或聊天机器人&#xff0c;经过训练&#xff0c;内容丰富且全面。Bard 接受过大量…