Skip to content

Instantly share code, notes, and snippets.

@FleshMobProductions
Last active January 10, 2023 21:48
Show Gist options
  • Save FleshMobProductions/c221b28a5cb11adee38bbc6dc16e39a6 to your computer and use it in GitHub Desktop.
Save FleshMobProductions/c221b28a5cb11adee38bbc6dc16e39a6 to your computer and use it in GitHub Desktop.
Highlight all scene objects that reference a selected object (needs to be of type UnityEngine.Object or a subclass of it)
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
namespace FMPUtils.Editor
{
public class SceneReferenceHighlighterWindow : EditorWindow
{
[System.Serializable]
public class ReferencingEntry
{
public string scenePath;
public UnityEngine.Object value;
}
private UnityEngine.Object target;
private List<Component> tempComponents = new List<Component>();
private ReferencingEntry[] referencingEntries = new ReferencingEntry[0];
private Vector2 scrollRectPos;
[MenuItem("FMPUtils/Tools/Scene Reference Highlighter")]
static void Init()
{
var window = (SceneReferenceHighlighterWindow)GetWindow(typeof(SceneReferenceHighlighterWindow));
window.Show();
}
void OnGUI()
{
EditorGUILayout.LabelField("Find Scene References", EditorStyles.boldLabel);
var targetPrev = target;
target = EditorGUILayout.ObjectField("Referenced Target", target, typeof(UnityEngine.Object), true);
if (targetPrev != target)
referencingEntries = new ReferencingEntry[0];
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope(target == null))
{
if (GUILayout.Button("Find Scene References"))
{
HighlightReferencingObjects();
}
}
if (referencingEntries.Length > 0)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Referencing Objects:", EditorStyles.boldLabel);
scrollRectPos = EditorGUILayout.BeginScrollView(scrollRectPos, false, true);
foreach (var entry in referencingEntries)
{
RenderReferenceEntry(entry);
}
EditorGUILayout.EndScrollView();
}
}
private void RenderReferenceEntry(ReferencingEntry entry)
{
EditorGUILayout.LabelField($"Scene Path: {entry.scenePath}", EditorStyles.wordWrappedLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Value:", GUILayout.MinWidth(30), GUILayout.MaxWidth(100));
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.ObjectField(entry.value, typeof(UnityEngine.Object), true);
}
EditorGUILayout.EndHorizontal();
}
private void HighlightReferencingObjects()
{
List<GameObject> referencingObjects = new List<GameObject>();
for (int sceneId = 0; sceneId < SceneManager.sceneCount; sceneId++)
{
var scene = SceneManager.GetSceneAt(sceneId);
foreach (var rootObject in scene.GetRootGameObjects())
{
CheckObjectRecursively(rootObject.transform, referencingObjects);
}
}
Debug.Log($"Found {referencingObjects.Count} objects referning '{target.name}': "
+ string.Join(",", referencingObjects.Select(x => x.name)));
if (referencingObjects.Count > 0)
{
Selection.objects = referencingObjects.ToArray();
}
referencingEntries = referencingObjects.Select(x => new ReferencingEntry { scenePath = GetGameObjectPath(x), value = x }).ToArray();
}
private void CheckObjectRecursively(Transform t, List<GameObject> referencingObjects)
{
if (t == null) return;
if (ReferencesTarget(t.gameObject))
referencingObjects.Add(t.gameObject);
int childCount = t.childCount;
for (int i = 0; i < childCount; i++)
{
Transform child = t.GetChild(i);
CheckObjectRecursively(child, referencingObjects);
}
}
// Currently only checks main fields and no serialized collections
private bool ReferencesTarget(GameObject gameObject)
{
tempComponents.Clear();
gameObject.GetComponents<Component>(tempComponents);
foreach (var component in tempComponents)
{
var componentSO = new SerializedObject(component);
// Get first seriaized property of SerializedObject
// like with other iterators, Next has to be called first to arrive at the first
// valid SerializedProperty
// Later we could check nested properties:
// https://forum.unity.com/threads/loop-through-serializedproperty-children.435119/
SerializedProperty currentProp = componentSO.GetIterator();
bool enterChildren = true;
while (currentProp.Next(enterChildren))
{
// Check if reference to an object that derives from UnityEngine.Object
if (currentProp.propertyType == SerializedPropertyType.ObjectReference)
{
if (currentProp.objectReferenceValue == target)
{
return true;
}
}
}
}
return false;
}
public static string GetGameObjectPath(GameObject obj)
{
string path = "/" + obj.name;
while (obj.transform.parent != null)
{
obj = obj.transform.parent.gameObject;
path = "/" + obj.name + path;
}
return path;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment