Skip to content

Instantly share code, notes, and snippets.

@Long18
Last active December 18, 2024 03:37
Show Gist options
  • Save Long18/290f36f88476ea69f1ffde1b0fb57312 to your computer and use it in GitHub Desktop.
Save Long18/290f36f88476ea69f1ffde1b0fb57312 to your computer and use it in GitHub Desktop.
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