#if UNITY_EDITOR using System.Collections.Generic; using System.Text; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; public static class MissingScriptUtility { [MenuItem("Tools/Diagnostics/Find Missing Scripts in Open Scenes")] public static void FindMissingScriptsInOpenScenes() { int missingCount = 0; var reported = new HashSet(); foreach (var scene in GetLoadedScenes()) { foreach (var root in scene.GetRootGameObjects()) ScanHierarchy(root, ref missingCount, reported); } Debug.Log($"[MissingScriptUtility] Missing scripts found: {missingCount}"); } [MenuItem("Tools/Diagnostics/Find Missing Scripts in Prefabs")] public static void FindMissingScriptsInPrefabs() { var prefabGuids = AssetDatabase.FindAssets("t:Prefab"); int missingCount = 0; for (int i = 0; i < prefabGuids.Length; i++) { var path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]); EditorUtility.DisplayProgressBar("Scanning Prefabs", path, (float)i / prefabGuids.Length); var root = PrefabUtility.LoadPrefabContents(path); if (root == null) continue; ScanHierarchy(root, ref missingCount, null, path); PrefabUtility.UnloadPrefabContents(root); } EditorUtility.ClearProgressBar(); Debug.Log($"[MissingScriptUtility] Missing scripts found in prefabs: {missingCount}"); } [MenuItem("Tools/Diagnostics/Remove Missing Scripts in Open Scenes")] public static void RemoveMissingScriptsInOpenScenes() { int removed = 0; foreach (var scene in GetLoadedScenes()) { foreach (var root in scene.GetRootGameObjects()) removed += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(root); } if (removed > 0) EditorSceneManager.MarkAllScenesDirty(); Debug.Log($"[MissingScriptUtility] Removed missing scripts: {removed}"); } [MenuItem("Tools/Diagnostics/Remove Missing Scripts in Prefabs")] public static void RemoveMissingScriptsInPrefabs() { var prefabGuids = AssetDatabase.FindAssets("t:Prefab"); int removed = 0; for (int i = 0; i < prefabGuids.Length; i++) { var path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]); EditorUtility.DisplayProgressBar("Cleaning Prefabs", path, (float)i / prefabGuids.Length); var root = PrefabUtility.LoadPrefabContents(path); if (root == null) continue; int removedInPrefab = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(root); if (removedInPrefab > 0) PrefabUtility.SaveAsPrefabAsset(root, path); removed += removedInPrefab; PrefabUtility.UnloadPrefabContents(root); } EditorUtility.ClearProgressBar(); if (removed > 0) AssetDatabase.SaveAssets(); Debug.Log($"[MissingScriptUtility] Removed missing scripts in prefabs: {removed}"); } private static IEnumerable GetLoadedScenes() { for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (scene.isLoaded) yield return scene; } } private static void ScanHierarchy(GameObject go, ref int missingCount, HashSet reported, string assetPath = null) { var components = go.GetComponents(); for (int i = 0; i < components.Length; i++) { if (components[i] == null) { missingCount++; if (reported == null || reported.Add(go)) { var label = string.IsNullOrWhiteSpace(assetPath) ? "scene object" : $"prefab {assetPath}"; Debug.LogWarning($"[MissingScriptUtility] Missing script on {label}: {GetHierarchyPath(go.transform)}"); } break; } } foreach (Transform child in go.transform) ScanHierarchy(child.gameObject, ref missingCount, reported); } private static string GetHierarchyPath(Transform transform) { var sb = new StringBuilder(transform.name); while (transform.parent != null) { transform = transform.parent; sb.Insert(0, transform.name + "/"); } return sb.ToString(); } } #endif