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。在这样的状况下,就须要再对那个文件作遍历。直到没有间接引用,才能获取真正全部的引用。

相关文章
相关标签/搜索