unity实现批量删除Prefab上Miss的脚本组件
感谢:https://www.cnblogs.com/AaronBlogs/p/7976054.html
我的unity版本: unity2017.2.0p4
本篇文章将根据我个人的实践进行记录,有些情况也许没有考虑进去。随着unity版本的提升,也许会有现成的api可以直接实现(像unity2019中的GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go),具体没有测试)
实现目标:
到 ,并且在后续的操作中图一的情况不会恢复。
先说明一下,miss脚本的两种情况:
在Inpsector面板中看起来是这样的,打开对应的prefab文件,继续往下看内在的区别。
执行工具后,prefab文件的变化成了下面的结构:
下面贴上代码:
1 using System; 2 using System.IO; 3 using System.Collections; 4 using System.Collections.Generic; 5 using UnityEngine; 6 using UnityEditor; 7 using System.Text.RegularExpressions; 8 9 namespace LgsProject 10 { 11 public partial class LgsTools_Optimization 12 { 13 [MenuItem("LgsTools/智能检测/Remove Missing-MonoBehavior Component")] 14 static public void RemoveMissComponent() 15 { 16 string fullPath = Application.dataPath + "/Art/Prefabs"; 17 fullPath = fullPath.Replace("/", @"\"); 18 //List<string> pathList = GetAssetsPathByFullPath(fullPath, "*.prefab", SearchOption.AllDirectories); 19 List<string> pathList = GetAssetsPathByRelativePath(new string[] { "Assets/Art/Prefabs" }, "t:Prefab", SearchOption.AllDirectories); 20 int counter = 0; 21 for (int i = 0, iMax = pathList.Count; i < iMax; i++) 22 { 23 EditorUtility.DisplayProgressBar("处理进度", string.Format("{0}/{1}", i + 1, iMax), (i + 1f) / iMax); 24 if (CheckMissMonoBehavior(pathList[i])) 25 ++counter; 26 } 27 EditorUtility.ClearProgressBar(); 28 EditorUtility.DisplayDialog("处理结果", "完成修改,修改数量 : " + counter, "确定"); 29 AssetDatabase.Refresh(); 30 } 31 32 /// <summary> 33 /// 获取项目中某种资源的路径 34 /// </summary> 35 /// <param name="fullPath">win的路径格式,以 "\"为分隔符</param> 36 /// <param name="filter">win的资源过滤模式 例如 : *.prefab</param> 37 /// <param name="searchOption">目录的搜索方式</param> 38 /// <returns></returns> 39 static List<string> GetAssetsPathByFullPath(string fullPath, string filter, SearchOption searchOption) 40 { 41 List<string> pathList = new List<string>(); 42 string[] files = Directory.GetFiles(fullPath, filter, searchOption); 43 for (int i = 0; i < files.Length; i++) 44 { 45 string path = files[i]; 46 path = "Assets" + path.Substring(Application.dataPath.Length, path.Length - Application.dataPath.Length); 47 pathList.Add(path); 48 } 49 50 return pathList; 51 } 52 53 54 /// <summary> 55 /// 获取项目中某种资源的路径 56 /// </summary> 57 /// <param name="relativePath">unity路径格式,以 "/" 为分隔符</param> 58 /// <param name="filter">unity的资源过滤模式 https://docs.unity3d.com/ScriptReference/AssetDatabase.FindAssets.html </param> 59 /// <param name="searchOption"></param> 60 /// <returns></returns> 61 static List<string> GetAssetsPathByRelativePath(string[] relativePath, string filter, SearchOption searchOption) 62 { 63 List<string> pathList = new List<string>(); 64 string[] guids = AssetDatabase.FindAssets(filter, relativePath); 65 for (int i = 0; i < guids.Length; i++) 66 { 67 string path = AssetDatabase.GUIDToAssetPath(guids[i]); 68 pathList.Add(path); 69 } 70 71 return pathList; 72 } 73 74 /// <summary> 75 /// 删除一个Prefab上的空脚本 76 /// </summary> 77 /// <param name="path">prefab路径 例Assets/Resources/FriendInfo.prefab</param> 78 static bool CheckMissMonoBehavior(string path) 79 { 80 bool isNull = false; 81 string textContent = File.ReadAllText(path); 82 Regex regBlock = new Regex("MonoBehaviour"); 83 // 以"---"划分组件 84 string[] strArray = textContent.Split(new string[] { "---" }, StringSplitOptions.RemoveEmptyEntries); 85 for (int i = 0; i < strArray.Length; i++) 86 { 87 string blockStr = strArray[i]; 88 if (regBlock.IsMatch(blockStr)) 89 { 90 // 模块是 MonoBehavior 91 //(?<名称>子表达式) 含义:将匹配的子表达式捕获到一个命名组中 92 Match guidMatch = Regex.Match(blockStr, "m_Script: {fileID: (.*), guid: (?<GuidValue>.*?), type: [0-9]}"); 93 if (guidMatch.Success) 94 { 95 string guid = guidMatch.Groups["GuidValue"].Value; 96 if (!File.Exists(AssetDatabase.GUIDToAssetPath(guid))) 97 { 98 isNull = true; 99 textContent = DeleteContent(textContent, blockStr); 100 } 101 } 102 103 Match fileIdMatch = Regex.Match(blockStr, @"m_Script: {fileID: (?<IdValue>\d+)}"); 104 if (fileIdMatch.Success) 105 { 106 string idValue = fileIdMatch.Groups["IdValue"].Value; 107 if (idValue.Equals("0")) 108 { 109 isNull = true; 110 textContent = DeleteContent(textContent, blockStr); 111 } 112 } 113 } 114 } 115 if (isNull) 116 { 117 // 有空脚本 写回prefab 118 File.WriteAllText(path, textContent); 119 } 120 return isNull; 121 } 122 123 // 删除操作 124 static string DeleteContent(string input, string blockStr) 125 { 126 input = input.Replace("---" + blockStr, ""); 127 Match idMatch = Regex.Match(blockStr, "!u!(.*) &(?<idValue>.*?)\n"); 128 if (idMatch.Success) 129 { 130 // 获取 MonoBehavior的fileID 131 string fileID = idMatch.Groups["idValue"].Value; 132 Regex regex = new Regex(" - (.*): {fileID: " + fileID + "}\n"); 133 input = regex.Replace(input, ""); 134 } 135 136 return input; 137 } 138 } 139 }
另外,在实践的过程中遇到了一个报错,顺便记录一下:
CheckConsistency: GameObject does not reference component MonoBehaviour. Fixing.
看下图,正常情况下prefab文件中区域1和区域2是对应的,如果不对应会报以上错误。