感謝: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是對應的,如果不對應會報以上錯誤。