【Unity】「FindReferencesInProject2」を元に少し改造しました。

DEVELOP, Unity

アセットの依存関係検索機能「FindReferencesInProject2」は高速に結果を表示しとても素晴らしいのですが、コンソールで結果が出るのみなので、もう少し使い勝手を良くしたいなと思いました。

目次

事前準備

FindReferencesInProject2 が動作する環境をまず準備してください。

ソースコード

元の FindReferencesInProject2 のソースコードを以下へ置き換えてください。その際にパスの修正も2点ほど書き換えてください。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEditor;
using UnityEngine;
 
namespace FindReferencesInProjectEx
{
    /// <summary>
    /// アセットが参照しているアセットをリストアップする。
    /// </summary>
    public class FindReferencesInProjectEx : EditorWindow
    {
        private const string MenuItemName = "Assets/Find References In Project %#&f";
        private const string MetaExtension = ".meta";
 
        private static UnityEngine.Object selectedObject = default;
        private static List<UnityEngine.Object> result = new List<UnityEngine.Object>();
        private Vector2 scrollPosition = Vector2.zero;
 
        [MenuItem(MenuItemName, false, 25)]
        public static void Find()
        {
            bool isMacOS = Application.platform == RuntimePlatform.OSXEditor;
            int totalWaitMilliseconds = isMacOS ? 2 * 1000 : 300 * 1000;
            int cpuCount = Environment.ProcessorCount;
            string appDataPath = Application.dataPath;
 
            selectedObject = Selection.activeObject;
            string selectedAssetPath = AssetDatabase.GetAssetPath(selectedObject);
            string selectedAssetGUID = AssetDatabase.AssetPathToGUID(selectedAssetPath);
            string selectedAssetMetaPath = selectedAssetPath + MetaExtension;
 
            result.Clear();
            var references = new List<string>();
            var output = new System.Text.StringBuilder();
 
            var stopwatch = new Stopwatch();
            stopwatch.Start();
 
            var psi = new ProcessStartInfo();
            psi.WindowStyle = ProcessWindowStyle.Minimized;
 
            if (isMacOS)
            {
                psi.FileName = "/usr/bin/mdfind";
                psi.Arguments = string.Format("-onlyin {0} {1}", appDataPath, selectedAssetGUID);
            }
            else
            {
                psi.FileName = Path.Combine(Environment.CurrentDirectory, @"Assets\Editor\FindReferencesInProjectEx\rg.exe");
                psi.Arguments = string.Format("--case-sensitive --follow --files-with-matches --no-text --fixed-strings " +
                                              "--ignore-file Assets/Editor/FindReferencesInProjectEx/ignore.txt " +
                                              "--threads {0} --regexp {1} -- {2}",
                    cpuCount, selectedAssetGUID, appDataPath);
            }
 
            psi.UseShellExecute = false;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
 
            var process = new Process();
            process.StartInfo = psi;
 
            process.OutputDataReceived += (sender, e) =>
            {
                if (string.IsNullOrEmpty(e.Data))
                    return;
 
                string relativePath = e.Data.Replace(appDataPath, "Assets").Replace("\\", "/");
 
                // skip the meta file of whatever we have selected
                if (relativePath == selectedAssetMetaPath)
                    return;
 
                references.Add(relativePath);
            };
 
            process.ErrorDataReceived += (sender, e) =>
            {
                if (string.IsNullOrEmpty(e.Data))
                    return;
 
                output.AppendLine("Error: " + e.Data);
            };
 
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
 
            while (!process.HasExited)
            {
                if (stopwatch.ElapsedMilliseconds < totalWaitMilliseconds)
                {
                    float progress = (float)((double)stopwatch.ElapsedMilliseconds / totalWaitMilliseconds);
                    string info = string.Format("Finding {0}/{1}s {2:P2}", stopwatch.ElapsedMilliseconds / 1000,
                        totalWaitMilliseconds / 1000, progress);
                    bool canceled = EditorUtility.DisplayCancelableProgressBar("Find References in Project", info, progress);
 
                    if (canceled)
                    {
                        process.Kill();
                        break;
                    }
 
                    Thread.Sleep(100);
                }
                else
                {
                    process.Kill();
                    break;
                }
            }
 
            foreach (string file in references)
            {
                string guid = AssetDatabase.AssetPathToGUID(file);
                output.AppendLine(string.Format("{0} {1}", guid, file));
 
                string assetPath = file;
                if (file.EndsWith(MetaExtension))
                {
                    assetPath = file.Substring(0, file.Length - MetaExtension.Length);
                }
 
                var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
                result.Add(asset);
                UnityEngine.Debug.Log(string.Format("{0}\n{1}", file, guid), asset);
            }
 
            EditorUtility.ClearProgressBar();
            stopwatch.Stop();
 
            GetWindow<FindReferencesInProjectEx>();
        }
 
        [MenuItem(MenuItemName, true)]
        private static bool FindValidate()
        {
            var obj = Selection.activeObject;
            if (obj != null && AssetDatabase.Contains(obj))
            {
                string path = AssetDatabase.GetAssetPath(obj);
                return !AssetDatabase.IsValidFolder(path);
            }
 
            return false;
        }
 
        /// <summary>
        /// 描画
        /// </summary>
        void OnGUI()
        {
            this.scrollPosition = GUILayout.BeginScrollView(this.scrollPosition);
 
            GUILayout.Label($"{selectedObject.name}の検索結果");
 
            if (result.Count > 0)
            {
                foreach (var referent in result)
                {
                    var iconSize = EditorGUIUtility.GetIconSize();
                    EditorGUIUtility.SetIconSize(Vector2.one * 16);
 
                    if (GUILayout.Button(referent.name))
                    {
                        Selection.objects = new[] { referent };
                    }
                    EditorGUIUtility.SetIconSize(iconSize);
                }
            }
            else
            {
                GUILayout.Label($"{selectedObject.name}はどこからも参照されていません。");
            }
 
            GUILayout.EndScrollView();
        }
    }
}

結果

Posted by kazupon