• Unity插件开发:SerializedObject/SerializedProperty——查找引用的资源


            Unity有一个Find References in Scene功能非常好用。在Project面板中右键一个文件,选择Find References in Scene就可以在场景中找到所有存在对这个文件有引用的物体。但是很多时候,我们更加需要知道的是,这个场景里面到底引用了哪些文件,比如做优化的时候。

            这里有一个插件叫做ResourceChecker,它可以列举出所有引用到的texture/mesh/mat等。但是他也有一个小小的问题,对于自定义脚本的引用没有效果。

            要实现这个需求,这里主要借助SerializedObject/SerializedProperty。参考官方文档:

            SerializedProperty在CustomEditor会经常用到,可以用于反射一个Unity对象的字段(甚至可以可以反射private字段,非常暴力)。

            在《Unity文件、文件引用、meta详解》一文中,曾经提到过unity资源序列化的数据要么是存在meta中,要么就是本身那个资源。比如用Notepad++打开prefab文件后,可能会是这样的yaml序列化数据。而这些数据都可以通过SerializedProperty获取得到。

    image

            在上图的例子中可以看到,Property名字为m_PrefabInternal,它的Value中,fileID不为0。由此可以看到这个物体引用了一个对象。

            下面,我们用代码来寻找一个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);
                    }
                }
            }
        }

           这里有几个API

    • 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中的所有字段,如果是对象引用的,并且对象的值是我们想要的类型,那么找出在工程中的路径,打印出来。

           我们扩充一下,增加以下函数

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

    个人博客请访问:http://www.cnblogs.com/CodeGize/
  • 相关阅读:
    [转贴]彻底解决 CrystalReports 登录失败问题。
    [资源]《就说》——《大学自习室》作者郝雨又一力作,继续那种搞笑的说唱
    [推荐]一个用来给控件做提示的JS脚本,鼠标移到控件上时出现提示,离开时消失!
    [原创]一个公告栏的源码(利用marquee作的)
    [转帖]DataGrid显示双层表头,即可实现合并单元格问题
    [转贴]15句让女生爱你一生的情话
    [疑问]您没有调试该服务器的权限。验证您是服务器上"Debuger Users"组的成员。
    [转帖]九个不可不占的便宜
    [转贴]秘芨(男孩子为了终身幸福,最好背下来哦)
    [转贴]也许放弃也是一种爱!++++++++觉得这篇文章写得不错
  • 原文地址:https://www.cnblogs.com/CodeGize/p/9180610.html
Copyright © 2020-2023  润新知