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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步