Last active
December 18, 2024 03:37
-
-
Save Long18/290f36f88476ea69f1ffde1b0fb57312 to your computer and use it in GitHub Desktop.
This file contains 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 UnityEngine; | |
using UnityEditor; | |
/// <summary> | |
/// Provides tools to find, log, select, and remove missing scripts in a Unity project. | |
/// Includes options to search active/inactive GameObjects and prefabs. | |
/// </summary> | |
public class FixMissingScript : EditorWindow | |
{ | |
/// <summary> | |
/// Determines whether inactive GameObjects should be included in the search. | |
/// </summary> | |
private bool includeInactive = true; | |
/// <summary> | |
/// Determines whether prefabs should be included in the search. | |
/// </summary> | |
private bool includePrefabs = true; | |
[MenuItem("Tools/FixMissingScript")] | |
public static void ShowWindow() | |
{ | |
EditorWindow.GetWindow(typeof(FixMissingScript)); | |
} | |
public void OnGUI() | |
{ | |
string includeInactiveTooltip = "Whether to include inactive GameObjects in the search."; | |
includeInactive = EditorGUILayout.Toggle(new GUIContent("Include Inactive", includeInactiveTooltip), includeInactive); | |
string includePrefabsTooltip = "Whether to include prefab GameObjects in the search."; | |
includePrefabs = EditorGUILayout.Toggle(new GUIContent("Include Prefabs", includePrefabsTooltip), includePrefabs); | |
if (GUILayout.Button("Log Missing Scripts")) | |
LogMissingScripts(UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects()); | |
if (GUILayout.Button("Log Missing Scripts from Selected GameObjects")) | |
LogMissingScripts(SelectedGameObjects(includeInactive, includePrefabs)); | |
EditorGUILayout.Space(); | |
if (GUILayout.Button("Select GameObjects with Missing Scripts")) | |
SelectGameObjectsWithMissingScripts(UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects()); | |
EditorGUILayout.Space(); | |
if (GUILayout.Button("Remove Missing Scripts")) | |
RemoveMissingScripts(UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects()); | |
if (GUILayout.Button("Remove Missing Scripts from Selected GameObjects")) | |
RemoveMissingScripts(SelectedGameObjects(includeInactive, includePrefabs)); | |
} | |
/// <summary> | |
/// Logs the number of missing scripts found across the provided GameObjects. | |
/// </summary> | |
/// <param name="gameObjects">An array of GameObjects to search for missing scripts.</param> | |
public static void LogMissingScripts(GameObject[] gameObjects) | |
{ | |
int gameObjectCount = 0; | |
int missingScriptCount = 0; | |
foreach (GameObject gameObject in gameObjects) | |
{ | |
missingScriptCount += GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject); | |
++gameObjectCount; | |
} | |
Debug.Log($"Searched {gameObjectCount} GameObjects and found {missingScriptCount} missing scripts."); | |
} | |
/// <summary> | |
/// Selects GameObjects with missing scripts from the provided GameObjects. | |
/// </summary> | |
/// <param name="gameObjects">An array of GameObjects to search for missing scripts.</param> | |
public static void SelectGameObjectsWithMissingScripts(GameObject[] gameObjects) | |
{ | |
List<GameObject> selections = new List<GameObject>(); | |
foreach (GameObject gameObject in gameObjects) | |
{ | |
if (GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject) > 0) | |
selections.Add(gameObject); | |
} | |
Selection.objects = selections.ToArray(); | |
} | |
/// <summary> | |
/// Removes missing scripts from the provided GameObjects. | |
/// </summary> | |
/// <param name="gameObjects">An array of GameObjects to remove missing scripts from.</param> | |
public static void RemoveMissingScripts(GameObject[] gameObjects) | |
{ | |
int missingScriptCount = 0; | |
foreach (GameObject gameObject in gameObjects) | |
{ | |
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject); | |
if (count > 0) | |
{ | |
Undo.RegisterCompleteObjectUndo(gameObject, "Remove missing scripts"); | |
GameObjectUtility.RemoveMonoBehavioursWithMissingScript(gameObject); | |
missingScriptCount += count; | |
} | |
} | |
Debug.Log($"Searched {gameObjects.Length} GameObjects and removed {missingScriptCount} missing scripts."); | |
} | |
#region Sub-utilities | |
/// <summary> | |
/// Returns all selected GameObjects, including their children and associated prefabs if specified. | |
/// </summary> | |
/// <param name="includingInactive">True to include inactive GameObjects; false to exclude them.</param> | |
/// <param name="includingPrefabs">True to include prefab GameObjects; false to exclude them.</param> | |
/// <returns>An array of selected GameObjects, including children and prefabs if specified.</returns> | |
public static GameObject[] SelectedGameObjects(bool includingInactive = true, bool includingPrefabs = true) | |
{ | |
List<GameObject> selectedGameObjects = new List<GameObject>(Selection.gameObjects); | |
foreach (GameObject selectedGameObject in Selection.gameObjects) | |
{ | |
Transform[] childTransforms = selectedGameObject.GetComponentsInChildren<Transform>(includingInactive); | |
foreach (Transform childTransform in childTransforms) | |
selectedGameObjects.Add(childTransform.gameObject); | |
if (includingPrefabs) | |
{ | |
HashSet<GameObject> prefabs = new HashSet<GameObject>(); | |
PrefabInstances(selectedGameObject, prefabs); | |
selectedGameObjects.AddRange(prefabs); | |
} | |
} | |
return selectedGameObjects.ToArray(); | |
} | |
/// <summary> | |
/// Recursively counts missing scripts in a GameObject and its children. | |
/// </summary> | |
/// <param name="gameObject">The root GameObject to search.</param> | |
/// <returns>The total number of missing scripts found.</returns> | |
public static int RecursiveMissingScriptCount(GameObject gameObject) | |
{ | |
int missingScriptCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject); | |
Transform[] childTransforms = gameObject.GetComponentsInChildren<Transform>(true); | |
foreach (Transform childTransform in childTransforms) | |
missingScriptCount += GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(childTransform.gameObject); | |
return missingScriptCount; | |
} | |
/// <summary> | |
/// Collects prefab instances associated with a GameObject recursively. | |
/// </summary> | |
/// <param name="instance">The GameObject instance to process.</param> | |
/// <param name="prefabs">A hash set to store collected prefabs.</param> | |
private static void PrefabInstances(GameObject instance, HashSet<GameObject> prefabs) | |
{ | |
GameObject source = PrefabUtility.GetCorrespondingObjectFromSource(instance); | |
if (source == null || !prefabs.Add(source)) | |
return; | |
PrefabInstances(source, prefabs); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment