-
-
Save restush/e551ec57ae47084e78b7b5f6be705c6a to your computer and use it in GitHub Desktop.
Unity Find missing references
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.Collections.Generic; | |
using UnityEditor; | |
using UnityEditor.UIElements; | |
using UnityEngine; | |
using UnityEngine.UIElements; | |
public class FindMissingEditor : EditorWindow | |
{ | |
private float _progress = 0f; | |
private bool _canceled; | |
private List<GameObject> _errorGameObjects = new List<GameObject>(); | |
[MenuItem(itemName: "Tools/Find Missing References")] | |
public static void Open() | |
{ | |
FindMissingEditor window = GetWindow<FindMissingEditor>("Find Missing Editor"); | |
Rect main = EditorGUIUtility.GetMainWindowPosition(); | |
Rect pos = window.position; | |
float centerWidth = (main.width - pos.width) * 0.5f; | |
float centerHeight = (main.height - pos.height) * 0.3f; | |
pos.x = main.x + centerWidth; | |
pos.y = main.y + centerHeight; | |
window.position = new Rect(pos.position.x, pos.position.y, pos.width, window._errorGameObjects.Count > 0 ? Mathf.Max(50, pos.height) : 50); | |
window.Show(); | |
} | |
private void CreateGUI() | |
{ | |
var hierarchyBtn = new Button() | |
{ | |
name = "Hierarchy-Button", | |
text = "Missing in Hierarchy...", | |
}; | |
var prefabBtn = new Button() | |
{ | |
name = "Prefab-Button", | |
text = "Missing in Prefabs..." | |
}; | |
var listView = new ListView() | |
{ | |
makeNoneElement = () => new VisualElement(), | |
makeItem = () => | |
{ | |
var objectField = new ObjectField(); | |
var selector = objectField.Q(className: "unity-object-field__selector"); | |
selector.RemoveFromClassList("unity-object-field__selector"); | |
return objectField; | |
}, | |
bindItem = (v, i) => | |
{ | |
var objectField = (v as ObjectField); | |
objectField.name = _errorGameObjects[i].name; | |
objectField.value = _errorGameObjects[i]; | |
}, | |
itemsSource = _errorGameObjects, | |
selectionType = SelectionType.None, | |
}; | |
listView.style.minHeight = 0; | |
listView.style.maxHeight = 1000; | |
hierarchyBtn.clicked += () => | |
{ | |
_errorGameObjects.Clear(); | |
int sceneCount = UnityEngine.SceneManagement.SceneManager.sceneCount; | |
_progress = 0f; | |
EditorUtility.DisplayProgressBar("Finding Missing References", "Searching in Hierarchy...", _progress); | |
listView.Q("unity-content-container").Clear(); | |
_canceled = false; | |
int totalIterator = sceneCount; | |
int currentIterator = 0; | |
for (int i = 0; i < sceneCount; ++i) | |
{ | |
var scene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i); | |
var roots = scene.GetRootGameObjects(); | |
var rootLength = roots.Length; | |
totalIterator += rootLength; | |
foreach (var go in roots) | |
{ | |
currentIterator++; | |
FindMissing(go, Iterator, AddToList, AddToList); | |
void AddToList(GameObject go) | |
{ | |
_errorGameObjects.Add(go); | |
} | |
void Iterator(GameObject gameObject) | |
{ | |
_progress = (float)currentIterator / totalIterator; | |
_canceled = EditorUtility.DisplayCancelableProgressBar("Finding Missing References", $"Searching in Hierarchy...`{gameObject.name}`", _progress); | |
} | |
if (_canceled) | |
break; | |
} | |
if (_canceled) | |
break; | |
currentIterator++; | |
} | |
EditorUtility.ClearProgressBar(); | |
UpdateUIAndAdjustWindowSize(listView); | |
}; | |
rootVisualElement.Add(listView); | |
rootVisualElement.Add(hierarchyBtn); | |
prefabBtn.clicked += () => | |
{ | |
_errorGameObjects.Clear(); | |
string[] guids = AssetDatabase.FindAssets("t:prefab"); | |
_progress = 0f; | |
_canceled = false; | |
EditorUtility.DisplayProgressBar("Finding Missing References", "Searching in Prefabs...", _progress); | |
var totalIterator = guids.Length; | |
var currentIterator = 0; | |
for (int i = 0; i < guids.Length; ++i) | |
{ | |
var path = AssetDatabase.GUIDToAssetPath(guids[i]); | |
var go = AssetDatabase.LoadAssetAtPath<GameObject>(path); | |
currentIterator++; | |
FindMissing(go, Iterator, AddToList, AddToList); | |
void AddToList(GameObject go) | |
{ | |
_errorGameObjects.Add(go); | |
} | |
void Iterator(GameObject gameObject) | |
{ | |
_progress = (float)currentIterator / totalIterator; | |
_canceled = EditorUtility.DisplayCancelableProgressBar("Finding Missing References", $"Searching in Prefabs...`{gameObject.name}`", _progress); | |
} | |
if (_canceled) | |
break; | |
} | |
EditorUtility.ClearProgressBar(); | |
UpdateUIAndAdjustWindowSize(listView); | |
}; | |
rootVisualElement.Add(prefabBtn); | |
} | |
private void FindMissing(GameObject go, System.Action<GameObject> iterator, System.Action<GameObject> missingScript, System.Action<GameObject> missingReference) | |
{ | |
var transforms = go.GetComponentsInChildren<Transform>(); | |
foreach (var t in transforms) | |
{ | |
iterator?.Invoke(t.gameObject); | |
int missingScripts = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(t.gameObject); | |
if (missingScripts > 0) | |
{ | |
Debug.LogError($"{t.gameObject.name} has missing script(s)", t.gameObject); | |
missingScript?.Invoke(t.gameObject); | |
} | |
var components = t.gameObject.GetComponents<MonoBehaviour>(); | |
foreach (var c in components) | |
{ | |
if (c == null) { continue; } | |
var serializedObject = new SerializedObject(c); | |
var it = serializedObject.GetIterator(); | |
while (it.NextVisible(true)) | |
{ | |
if (it.propertyType == SerializedPropertyType.ObjectReference) | |
{ | |
if (it.objectReferenceValue == null && it.objectReferenceInstanceIDValue != 0) | |
{ | |
Debug.LogError($"{t.gameObject.name}.{c.GetType().Name}.{it.displayName} has missing reference", c); | |
missingReference?.Invoke(t.gameObject); | |
} | |
} | |
} | |
} | |
} | |
} | |
private void UpdateUIAndAdjustWindowSize(ListView listView) | |
{ | |
var hasError = _errorGameObjects.Count > 0; | |
if (hasError) | |
{ | |
listView.style.minHeight = new StyleLength(Length.Auto()); | |
listView.style.paddingBottom = 8; | |
listView.style.paddingTop = 8; | |
listView.style.paddingLeft = 8; | |
listView.style.paddingRight = 8; | |
} | |
else | |
{ | |
listView.style.minHeight = 0; | |
listView.style.paddingBottom = 0; | |
listView.style.paddingTop = 0; | |
listView.style.paddingLeft = 0; | |
listView.style.paddingRight = 0; | |
} | |
listView.Rebuild(); | |
if (hasError) | |
{ | |
var listViewCurrentHeight = _errorGameObjects.Count * 20; | |
var maxWindowHeight = 200; | |
listViewCurrentHeight = Mathf.Clamp(listViewCurrentHeight, 0, maxWindowHeight); | |
position = new Rect(position.x, position.y, position.width, height: Mathf.Max(listViewCurrentHeight + 50, position.height)); | |
} | |
else | |
{ | |
position = new Rect(position.x, position.y, position.width, Mathf.Max(50, position.height)); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment