开头很简单,最难的是坚持。|

陈侠云

园龄:2年10个月粉丝:1关注:1

Unity 查找资源间的引用

前言

最近音频同事反馈说,给他们写的编辑器中,音频挂载在预制体当中了却查找不到Wwise的引用。
所以我回顾了之前的代码,提炼出比较核心的代码给大家分享下,但在项目后期,面对大量对象的情况下,其实性能还是不太行的,如果有人有更好的建议也可以在评论里分享一下。
废话不多说,开始!


SerializedObject和SerializedProperty

思路的核心是,找到gameObject中所有组件的引用对象,这里Unity官方很贴心的给我们准备了2个利器,分别是SerializedObject和SerializedProperty,它俩的作用是编辑Unity对象上的可序列化字段。

    /// <summary>
    /// 找到指定组件中所有的SerializedProperty属性
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    private List<SerializedProperty> GetSerializedProperties(UnityEngine.Object obj)
    {
        var so = new SerializedObject(obj);
        so.Update();

        var result = new List<SerializedProperty>();
        SerializedProperty iterator = so.GetIterator();
        while (iterator.NextVisible(true))
        {
            SerializedProperty copy = iterator.Copy();
            if (iterator.isArray)
            {
                result.AddRange(GetSOArray(copy));
            }
            else
            {
                result.Add(copy);
            }
        }

        return result;
    }

    private List<SerializedProperty> GetSOArray(SerializedProperty property)
    {
        int size = property.arraySize;
        var result = new List<SerializedProperty>();

        for (int i = 0; i < size; i++)
        {
            var iterator = property.GetArrayElementAtIndex(i);
            var copy = property.Copy();
            
            if (iterator.isArray)
            {
                result.AddRange(GetSOArray(copy));   
            }
            else
            {
                result.Add(copy);
            }
        }

        return result;
    }

得到所有的SerializedProperty后,我们就可以判断其中是否有我们需要的序列化对象了

Demo代码

// Copyright (c) 2024 陈侠云. All rights reserved.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;

public class SimpleFindRefTool : OdinEditorWindow
{
    private string findObjHash;
    private string[] files;
    private Dictionary<string, List<Info>> refGuid2Infos = new Dictionary<string, List<Info>>();
    
    [Serializable]
    private class Info
    {
        public string guid;
        public string assetPath;
        public string componentType;
        public string property;
    }
    
    [MenuItem("陈侠云Tools/多线程加速资源查找")]
    private static void ShowWindow()
    {
        var window = GetWindow<SimpleFindRefTool>();
        window.titleContent = new GUIContent("查找资源引用");
        window.目录 = $"{Application.dataPath}/多线程加速资源查找/资源";
        window.Show();
    }

    #region 布局

    [Sirenix.OdinInspector.ReadOnly] public string 目录;
    [AssetsOnly] public UnityEngine.GameObject 资源;
    [Button]
    public void 搜索()
    {
        refGuid2Infos.Clear();
        if (资源 == null)
        {
            Debug.LogError("查找资源不存在");
            return;
        }

        findObjHash = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(资源));
        files = Directory.GetFiles(目录, "*.*", SearchOption.AllDirectories)
            .Where(file => Path.GetExtension(file).ToLower() == ".prefab").ToArray();
        
        CollectSingleRes(files);
        if (refGuid2Infos.TryGetValue(findObjHash, out var infos))
        {
            for (int i = 0, icnt = infos.Count; i < icnt; i++)
            {
                var info = infos[i];
                Debug.Log($"预制体路径:{info.assetPath} 组件:{info.componentType} 属性:{info.property} 存在“{资源.name}”的引用");
            }
        }
        refGuid2Infos.Clear();
        Debug.Log("查找完毕");
    }
    #endregion

    private void CollectSingleRes(string[] filePaths)
    {
        for (int i = 0; i < filePaths.Length; i++)
        {
            EditorUtility.DisplayProgressBar("资源查找", $"查找资源中({i}/{filePaths.Length})...", ((float)i / (float)filePaths.Length));
            string assetPath = filePaths[i].Replace(Application.dataPath + "/", "Assets\\");
            LoadSerialized(assetPath);
        }
        
        EditorUtility.ClearProgressBar();
    }

    /// <summary>
    /// 找到指定资源中所有引用到的GUID
    /// </summary>
    /// <param name="assetPath"></param>
    /// <returns></returns>
    private void LoadSerialized(string assetPath)
    {
        List<Info> list = new List<Info>();
        UnityEngine.Object assetData = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object));
        var gameObject = assetData as GameObject;
        if (gameObject != null)
        {
            Component[] components = gameObject.GetComponentsInChildren<Component>(true);
            for (int i = 0; i < components.Length; i++)
            {
                LoadSingleSerializedAsset(components[i], assetPath);
            }
        }
    }

    /// <summary>
    /// 找到指定组件中所有引用到的GUID
    /// </summary>
    /// <param name="component"></param>
    /// <returns></returns>
    private void LoadSingleSerializedAsset(Component component, string asssetPath)
    {
        List<SerializedProperty> properties = GetSerializedProperties(component);

        for (int i = 0, icnt = properties.Count; i < icnt; i++)
        {
            if (properties[i].propertyType != SerializedPropertyType.ObjectReference)
            {
                continue;
            }

            var refObj = properties[i].objectReferenceValue;
            if (refObj == null)
            {
                continue;
            }

            string refPath = AssetDatabase.GetAssetPath(refObj);
            string refGuid = AssetDatabase.AssetPathToGUID(refPath);

            if (!refGuid2Infos.TryGetValue(refGuid, out _))
            {
                refGuid2Infos[refGuid] = new List<Info>();
            }
            refGuid2Infos[refGuid].Add(new Info()
            {
                guid = refGuid,
                assetPath = asssetPath,
                componentType = component.GetType().Name,
                property = properties[i].name
            });
        }
    }

    /// <summary>
    /// 找到指定组件中所有的SerializedProperty属性
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    private List<SerializedProperty> GetSerializedProperties(UnityEngine.Object obj)
    {
        var so = new SerializedObject(obj);
        so.Update();

        var result = new List<SerializedProperty>();
        SerializedProperty iterator = so.GetIterator();
        while (iterator.NextVisible(true))
        {
            SerializedProperty copy = iterator.Copy();
            if (iterator.isArray)
            {
                result.AddRange(GetSOArray(copy));
            }
            else
            {
                result.Add(copy);
            }
        }

        return result;
    }

    private List<SerializedProperty> GetSOArray(SerializedProperty property)
    {
        int size = property.arraySize;
        var result = new List<SerializedProperty>();

        for (int i = 0; i < size; i++)
        {
            var iterator = property.GetArrayElementAtIndex(i);
            var copy = property.Copy();
            
            if (iterator.isArray)
            {
                result.AddRange(GetSOArray(copy));   
            }
            else
            {
                result.Add(copy);
            }
        }

        return result;
    }
}

本文作者:陈侠云

本文链接:https://www.cnblogs.com/chenxiayun/p/18140270

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   陈侠云  阅读(201)  评论(0编辑  收藏  举报
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起