前言
最近音訊同事反饋說,給他們寫的編輯器中,音訊掛載在預製體當中了卻查詢不到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;
}
}