對於資源上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,然后問題成功解決!

  上面的代碼僅供參考,並且此處貼出編輯器處理完之后的報錯,希望知道此問題的人能夠留言幫我解答一下原因:

  

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM