Unity插件開發:SerializedObject/SerializedProperty——查找引用的資源

        Unity有一個Find References in Scene功能很是好用。在Project面板中右鍵一個文件,選擇Find References in Scene就能夠在場景中找到全部存在對這個文件有引用的物體。可是不少時候,咱們更加須要知道的是,這個場景裏面到底引用了哪些文件,好比作優化的時候。html

        這裏有一個插件叫作ResourceChecker,它能夠列舉出全部引用到的texture/mesh/mat等。可是他也有一個小小的問題,對於自定義腳本的引用沒有效果。git

        要實現這個需求,這裏主要藉助SerializedObject/SerializedProperty。參考官方文檔:github

        SerializedProperty在CustomEditor會常常用到,能夠用於反射一個Unity對象的字段(甚至能夠能夠反射private字段,很是暴力)。函數

        在《Unity文件、文件引用、meta詳解》一文中,曾經提到過unity資源序列化的數據要麼是存在meta中,要麼就是自己那個資源。好比用Notepad++打開prefab文件後,可能會是這樣的yaml序列化數據。而這些數據均可以經過SerializedProperty獲取獲得。優化

image

        在上圖的例子中能夠看到,Property名字爲m_PrefabInternal,它的Value中,fileID不爲0。由此能夠看到這個物體引用了一個對象。spa

        下面,咱們用代碼來尋找一個Prefab中引用到指定類型的全部文件,好比Sprite。插件

    public static void FindRef<T>(SerializedObject obj) where T : Object
    {
        var prop = obj.GetIterator();
        while (prop.Next(true))
        {
            if (prop.propertyType == SerializedPropertyType.ObjectReference)
            {
                var value = prop.objectReferenceValue;
                if (value is T)
                {
                    var path = AssetDatabase.GetAssetPath(value);
                    Debug.Log(prop.propertyPath + ":" + path);
                }
            }
        }
    }

       這裏有幾個API3d

  • SerializedObject.GetIterator:獲取這個SerializedObject上的初始SerializedProperty。用這個SerializedProperty能夠逐個列舉出下一個SerializedProperty
  • SerializedProperty.Next:獲取下一個SerializedProperty,若是參數爲true,就會進入子字段Object的Property.好比一個mono腳本上有A字段,A字段是一個class,它有本身若干個可序列的字段,那麼就會逐個列舉這些字段的SerializedProperty。
  • SerializedProperty.propertyType:字段的類型,好比是引用,數字,字符等等,見SerializedPropertyType。每一種Type對應的值的類型也是不同
  • SerializedProperty.objectReferenceValue,引用類型的字段的值。因爲這裏咱們主要查找的是對文件的引用,因此引用的對象確定在objectReferenceValue中
  • SerializedProperty.propertyPath,字段在這個SerializedObject中的路徑。正如上面所言,這個字段多是屬於子字段的,因此有路徑之說

       因此,這段代碼就是,逐個遍歷一個SerializedObject中的全部字段,若是是對象引用的,而且對象的值是咱們想要的類型,那麼找出在工程中的路徑,打印出來。code

       咱們擴充一下,增長如下函數orm

    public static void FindRefWithGameObject<T>(GameObject obj) where T : Object
    {
        var coms = obj.GetComponentsInChildren<Component>();
        foreach (var com in coms)
        {
            var so = new SerializedObject(com);
            FindRef<T>(so);
        }
    }

        很簡單,找到一個GameObject物體下的全部組件,轉爲SerializedObject,而後用以前的函數,把每個組件下,引用到指定T類型的資源的字段所有打印出來。

        最後,咱們真正來找出全部引用到的Sprite

    [MenuItem("Assets/Test/FindSprite")]
    public static void FindSprite()
    {
        var select = Selection.activeGameObject;
        if (select == null)
            return;
        FindRefWithGameObject<Sprite>(select);
    }

       OK,在一個Prefab上右鍵,選擇Test->FindSprite,能夠看到打印出全部的引用信息了

      在這個demo中能夠看到包含了Image控件中的Sprite引用,Button中切換的Sprite引用,自定義腳本Test中對Sprite的引用

image

       至此完成了大部分的引用信息獲取。這裏還有一個問題,沒有間接引用的信息,好比,Test腳本中引用一個Animation或者其餘文件,而這些文件又引用到Sprite。在這樣的狀況下,就須要再對那個文件作遍歷。直到沒有間接引用,才能獲取真正全部的引用。

相關文章
相關標籤/搜索