using UnityEngine; using UnityEditor; using System.Collections.Generic; public class MissingScriptUtility : EditorWindow { Vector2 scroll; List projectObjectsWithMissingScripts = new List(); int sceneMissingCount = 0; [MenuItem("Tools/Missing Script Utility")] static void Init() { GetWindow("Missing Script Utility"); } void OnGUI() { GUILayout.Label("Scene Scan", EditorStyles.boldLabel); if (GUILayout.Button("Find Missing Scripts in Scene")) { sceneMissingCount = ScanScene(); } EditorGUILayout.LabelField($"Missing scripts in scene: {sceneMissingCount}"); if (GUILayout.Button("Remove Missing Scripts from Selected GameObjects")) { RemoveFromSelectedInScene(); } GUILayout.Space(20); GUILayout.Label("Project Scan (Assets)", EditorStyles.boldLabel); if (GUILayout.Button("Scan Entire Project for Missing Scripts")) { ScanProject(); } EditorGUILayout.LabelField($"Assets with missing scripts: {projectObjectsWithMissingScripts.Count}"); if (projectObjectsWithMissingScripts.Count > 0) { scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Height(150)); foreach (var obj in projectObjectsWithMissingScripts) { EditorGUILayout.ObjectField(obj, typeof(Object), false); } EditorGUILayout.EndScrollView(); } if (GUILayout.Button("Remove Missing Scripts from Selected Assets")) { RemoveFromSelectedAssets(); } if (GUILayout.Button("Auto-fix All Prefabs in Project (Remove Missing Scripts)")) { bool confirm = EditorUtility.DisplayDialog( "Are you sure?", "This will permanently remove all missing script references from ALL prefab assets in your project.\n\nThis action cannot be undone. Do you want to continue?", "Yes, proceed", "Cancel" ); if (confirm) { AutoFixAllPrefabs(); } } } int ScanScene() { int count = 0; List objectsWithMissing = new List(); GameObject[] go = GameObject.FindObjectsOfType(); foreach (GameObject g in go) { Component[] components = g.GetComponents(); for (int i = 0; i < components.Length; i++) { if (components[i] == null) { Debug.LogWarning("Missing script on GameObject: " + g.name, g); objectsWithMissing.Add(g); count++; break; } } } if (objectsWithMissing.Count > 0) { Selection.objects = objectsWithMissing.ToArray(); // Select in editor Debug.Log($"Selected {objectsWithMissing.Count} objects with missing scripts."); } else { Debug.Log("No missing scripts found in scene."); } return count; } void RemoveFromSelectedInScene() { GameObject[] selectedObjects = Selection.gameObjects; int totalRemoved = 0; foreach (GameObject go in selectedObjects) { Undo.RegisterCompleteObjectUndo(go, "Remove Missing Scripts"); int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go); if (removed > 0) { Debug.Log($"Removed {removed} missing scripts from: {go.name}", go); totalRemoved += removed; } } Debug.Log($"Done. Removed total {totalRemoved} missing script components from selection."); } void ScanProject() { projectObjectsWithMissingScripts.Clear(); string[] allGUIDs = AssetDatabase.FindAssets("t:Prefab t:Scene t:ScriptableObject"); foreach (string guid in allGUIDs) { string path = AssetDatabase.GUIDToAssetPath(guid); Object obj = AssetDatabase.LoadMainAssetAtPath(path); if (obj == null) continue; GameObject go = obj as GameObject; if (go != null) { if (HasMissingScript(go)) projectObjectsWithMissingScripts.Add(go); } else { var contents = AssetDatabase.LoadAllAssetsAtPath(path); foreach (var item in contents) { if (item == null) continue; SerializedObject so = new SerializedObject(item); if (HasMissingScriptSerialized(so)) { projectObjectsWithMissingScripts.Add(item); break; } } } } Debug.Log($"Project scan complete. Found {projectObjectsWithMissingScripts.Count} assets with missing scripts."); } bool HasMissingScript(GameObject go) { Component[] components = go.GetComponentsInChildren(true); foreach (Component comp in components) { if (comp == null) return true; } return false; } bool HasMissingScriptSerialized(SerializedObject so) { SerializedProperty prop = so.FindProperty("m_Script"); return prop != null && prop.objectReferenceValue == null; } void RemoveFromSelectedAssets() { foreach (var obj in Selection.objects) { GameObject go = obj as GameObject; if (go == null) continue; string path = AssetDatabase.GetAssetPath(go); GameObject instance = PrefabUtility.LoadPrefabContents(path); int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(instance); if (removed > 0) { Debug.Log($"Removed {removed} missing scripts from prefab: {go.name}", go); PrefabUtility.SaveAsPrefabAsset(instance, path); } PrefabUtility.UnloadPrefabContents(instance); } AssetDatabase.SaveAssets(); } void AutoFixAllPrefabs() { string[] prefabGUIDs = AssetDatabase.FindAssets("t:Prefab"); int totalFixed = 0; foreach (string guid in prefabGUIDs) { string path = AssetDatabase.GUIDToAssetPath(guid); GameObject prefab = AssetDatabase.LoadAssetAtPath(path); if (prefab == null) continue; GameObject instance = PrefabUtility.LoadPrefabContents(path); int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(instance); if (removed > 0) { PrefabUtility.SaveAsPrefabAsset(instance, path); totalFixed += removed; Debug.Log($"Auto-fixed prefab '{path}' – removed {removed} missing scripts."); } PrefabUtility.UnloadPrefabContents(instance); } AssetDatabase.SaveAssets(); Debug.Log($"Auto-fix complete. Removed missing scripts from {totalFixed} components."); } }