对于资源上MissingScript的清理方案讨论
Unity工程随着复杂度的提升,常会有Prefab上的脚本丢失的情况,如下图所示:
首先失去关联的脚本,是没有线索找到原来是什么文件的,那么有没有办法批处理将这些MissingScript进行一下清理呢?就我使用的Unity4.6所提供的接口来说,没有非常完美的解决办法,但有一些还算可以用来解决问题的做法。
方法1:找到所有包含有Missing脚本的Prefab,然后逐个手动删除。
方法2:通过代码自动批处理解决。
第一种方法比较笨拙,对于资源量不是很大的工程,可以这样做,查找Missing脚本Prefab的代码如下:
using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; public class ClearMissingScript { [MenuItem("Custom/SelectMissing")] static void SelectMissing(MenuCommand command) { string []guids=AssetDatabase.FindAssets("t:Prefab", ["Assets/PrefabDir"]); List<GameObject> selection = new List<GameObject>(); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); GameObject obj=AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; Component[] cs = obj.GetComponents<Component>(); foreach (Component c in cs) { if (c == null) { selection.Add(obj); } } } Selection.objects = selection.ToArray(); } }
上面的脚本会帮你自动选中所有的包含脚本丢失的Prefab,有耐心的话就挨个处理吧...
第二种方法会智能一下,但是有一些问题,不确定是不是我代码写的有问题哈,代码如下所示:
public class CleanUpMissingScripts { [MenuItem("Edit/CleanupMissingScripts &c")] public static void CleanupMissingScripts() { for(int i = 0; i < Selection.gameObjects.Length; ++i) { var gameObject = Selection.gameObjects[i]; var components = gameObject.GetComponents<Component>(); SerializedObject serializedObject = new SerializedObject(gameObject); SerializedProperty prop = serializedObject.FindProperty("m_Component"); int r = 0; for(int j = 0; j < components.Length; j++) { if(components[j] == null) { prop.DeleteArrayElementAtIndex(j - r); r++; } } serializedObject.ApplyModifiedProperties(); } AssetDatabase.Refresh(); } }
这个脚本利用SerializedObject和SerializedProperty两个类来进行处理,思路上比较清晰,但是有问题:处理完之后看似清除了所有的Missing脚本,但一运行游戏,这些Missing脚本又回来了,于是换个思路,我们先将处理asset实例化出来,然后对实例化后的GameObject进行处理,再将处理完毕的GameObject写进asset,代码如下所示:
public class MissingScriptsEditor : EditorWindow { private static EditorWindow window; private List<GameObject> lstTmp = new List<GameObject>(); [MenuItem("Custom/MissingScripteEditor")] private static void Execute() { if (window == null) window = (MissingScriptsEditor)GetWindow(typeof(MissingScriptsEditor)); window.Show(); } private void OnGUI() { GUILayout.BeginVertical("box"); if (GUILayout.Button("CleanUpSelection", GUILayout.Height(30f))) { CleanUpSelection(); } GUILayout.EndVertical(); } private void CleanUpSelection() { var lstSelection = Selection.GetFiltered(typeof(GameObject), SelectionMode.DeepAssets); for(int i = 0; i < lstSelection.Length; ++i) { EditorUtility.DisplayProgressBar("Checking", "逐个分析中,请勿退出!", (float)i / (float)lstSelection.Length); var gameObject = lstSelection[i] as GameObject; var components = gameObject.GetComponents<Component>(); for (int j = 0; j < components.Length; j++) { // 如果组建为null if (components[j] == null) { CleanUpAsset(gameObject); break; } } } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); foreach (var go in lstTmp) { GameObject.DestroyImmediate(go); } lstTmp.Clear(); } private void CleanUpAsset(Object asset) { GameObject go = PrefabUtility.InstantiatePrefab(asset) as GameObject; // 创建一个序列化对象 SerializedObject serializedObject = new SerializedObject(go); // 获取组件列表属性 SerializedProperty prop = serializedObject.FindProperty("m_Component"); var components = go.GetComponents<Component>(); int r = 0; for (int j = 0; j < components.Length; j++) { // 如果组建为null if (components[j] == null) { // 按索引删除 prop.DeleteArrayElementAtIndex(j - r); r++; } } // 应用修改到对象 serializedObject.ApplyModifiedProperties(); // 将数据替换到asset // PrefabUtility.ReplacePrefab(go, asset); PrefabUtility.CreatePrefab(AssetDatabase.GetAssetPath(asset), go); go.hideFlags = HideFlags.HideAndDontSave; // 删除临时实例化对象 lstTmp.Add(go); } }
此代码提供了简单的编辑器界面,对于在Project中选中的Prefab进行处理,处理完之后可以很干净的删除所有的MissingScript引用,但是在Ctrl+S或运行游戏的时候会出现一些额外的提示,或者会直接崩掉。但是,重新开启Unity之后就好了,并且问题也解决了。
所以建议使用这种方法,并且在处理完之后重启一下Unity,然后问题成功解决!
上面的代码仅供参考,并且此处贴出编辑器处理完之后的报错,希望知道此问题的人能够留言帮我解答一下原因: