Files
BABA_YAGA/Assets/Editor/ReferenceFinderTool.cs
2026-03-26 20:27:19 +07:00

161 lines
5.6 KiB
C#

// ===============================================================================
// ReferenceFinderTool - Deep Dependency Analysis for Unity Assets
//
// Creator: Scove
// Last Updated: 2024-05-08
// Version: 2.0
//
// Purpose:
// Finds every asset in the project that references the selected item by
// scanning GUIDs inside Unity's YAML-based files (Scenes, Prefabs, Materials, etc.).
//
// Key Features:
// 1. Interactive Result List: Click any result to highlight the asset in Project window.
// 2. Wide Scope: Scans all text-based assets (Mat, PhysMat, Controller, Asset, etc.).
// 3. Optimized Search: Faster file reading and progress bar with Cancel support.
// 4. Clean UI: Integrated as a professional Editor Window.
//
// How to Use:
// 1. Right-click any asset in the Project window.
// 2. Select "Find References (Deep Scan)".
// 3. View the results in the pop-up window.
// ===============================================================================
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
namespace Editor
{
public class ReferenceFinderTool : EditorWindow
{
private static List<string> foundPaths = new List<string>();
private static string targetAssetName = "";
private static string targetAssetGUID = "";
private Vector2 scrollPosition;
[MenuItem("Assets/Find References (Deep Scan)", false, 25)]
public static void FindReferences()
{
Object selected = Selection.activeObject;
if (selected == null) return;
string path = AssetDatabase.GetAssetPath(selected);
targetAssetGUID = AssetDatabase.AssetPathToGUID(path);
targetAssetName = selected.name;
if (string.IsNullOrEmpty(targetAssetGUID)) return;
PerformDeepScan();
// Open the results window
ReferenceFinderTool window = GetWindow<ReferenceFinderTool>("Reference Finder");
window.minSize = new Vector2(400, 300);
window.Show();
}
private static void PerformDeepScan()
{
foundPaths.Clear();
// We look for all assets that are usually saved in Text/YAML format
// Added: Materials, Animators, ScriptableObjects, etc.
string[] allGuids = AssetDatabase.FindAssets("t:Prefab t:Scene t:Material t:PhysicMaterial t:RuntimeAnimatorController t:ScriptableObject t:AnimatorOverrideController");
int total = allGuids.Length;
for (int i = 0; i < total; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(allGuids[i]);
// Update Progress Bar
bool isCanceled = EditorUtility.DisplayCancelableProgressBar(
"Searching References",
$"Scanning: {Path.GetFileName(assetPath)}",
(float)i / total);
if (isCanceled) break;
// Check if the asset file contains the Target GUID
if (FileContainsGUID(assetPath, targetAssetGUID))
{
foundPaths.Add(assetPath);
}
}
EditorUtility.ClearProgressBar();
}
private static bool FileContainsGUID(string path, string guid)
{
if (!File.Exists(path)) return false;
// Using StreamReader is faster than File.ReadAllText for very large files
using (StreamReader reader = new StreamReader(path))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains(guid)) return true;
}
}
return false;
}
private void OnGUI()
{
GUILayout.Label($"References for: {targetAssetName}", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"GUID: {targetAssetGUID}", EditorStyles.miniLabel);
EditorGUILayout.Space();
if (foundPaths.Count == 0)
{
EditorGUILayout.HelpBox("No references found. Note: Ensure 'Asset Serialization' is set to 'Force Text' in Project Settings.", MessageType.Info);
}
else
{
GUILayout.Label($"Found {foundPaths.Count} items:", EditorStyles.label);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
for (int i = 0; i < foundPaths.Count; i++)
{
DrawResultItem(foundPaths[i]);
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.Space();
if (GUILayout.Button("Rescan", GUILayout.Height(30)))
{
PerformDeepScan();
}
}
private void DrawResultItem(string path)
{
EditorGUILayout.BeginHorizontal("box");
// Get the asset icon
Texture icon = AssetDatabase.GetCachedIcon(path);
GUILayout.Label(icon, GUILayout.Width(16), GUILayout.Height(16));
// Display path
GUILayout.Label(path, EditorStyles.wordWrappedLabel);
GUILayout.FlexibleSpace();
// Ping Button
if (GUILayout.Button("Ping", GUILayout.Width(50)))
{
Object obj = AssetDatabase.LoadAssetAtPath<Object>(path);
EditorGUIUtility.PingObject(obj);
Selection.activeObject = obj;
}
EditorGUILayout.EndHorizontal();
}
}
}