Last active
January 10, 2023 21:48
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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