Skip to content

How to Remove Missing Scripts Programmatically in Unity

Removing missing components can be a time consuming hassle in Unity.

Luckily a few little known utilities will make it easy.

GameObjectUtility.RemoveMonoBehavioursWithMissingScript is an editor method that will remove all missing scripts from the input GameObject.

Code Example

This code example will create an editor window that can be used to count missing scripts, select the game objects containing missing scripts, and remove the missing scripts.

using System.Collections.Generic;

using UnityEngine;
using UnityEditor;
public class MissingScriptUtility : EditorWindow
{
    bool includeInactive = true;
    bool includePrefabs = true;

    [MenuItem("Window/MissingScriptUtility")]
    public static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(MissingScriptUtility));
    }

    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));
    }

    public static void LogMissingScripts(GameObject[] gameObjects)
    {
        int gameObjectCount = 0;
        int missingScriptCount = 0;
        foreach (GameObject gameObject in gameObjects)
        {
            missingScriptCount += GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject);
            ++gameObjectCount;
        }

        Debug.Log(string.Format("Searched {0} GameObjects and found {1} missing scripts.", gameObjectCount, missingScriptCount));
    }

    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();
    }

    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(string.Format("Searched {0} GameObjects and removed {1} missing scripts.", gameObjects.Length, missingScriptCount));
    }

    #region Sub-utilities

    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();
    }

    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;
    }

    private static void PrefabInstances(GameObject instance, HashSet<GameObject> prefabs)
    {
        GameObject source = PrefabUtility.GetCorrespondingObjectFromSource(instance);
        // Only visit if source is valid, and hasn't been visited before
        if (source == null || !prefabs.Add(source))
            return;

        PrefabInstances(source, prefabs);
    }

    #endregion
}

“It is not allowed to modify the data property” error

If you got this error, you likely rolled your own solution. Using the GameObjectUtility.RemoveMonoBehavioursWithMissingScript method as shown in the example above will solve it.

Published inCode ExamplesDevelopment Tips

2 Comments

  1. Thank you for posting your code. There is a bug in line 94 and 99. You can’t modify the array you iterating through. This would be my fix:

    protected static GameObject[] SelectedGameObjects(bool includingInactive = true, bool includingPrefabs = true)
    {
    var selectedGameObjects = new List();

    foreach (var go in Selection.gameObjects)
    {
    var childTransforms = go.GetComponentsInChildren(includingInactive);

    foreach (var t in childTransforms)
    selectedGameObjects.Add(t.gameObject);

    if (includingPrefabs)
    {
    var prefabs = new HashSet();

    PrefabInstances(go, prefabs);

    selectedGameObjects.AddRange(prefabs);
    }
    }

    return selectedGameObjects.ToArray();
    }

    • Jacob Jacob

      Good catch! I’ve switched the iterated list to Selection.gameObjects.

      If you prefer the var style of coding, I believe you need to modify new List(); to be new List(Selection.gameObjects);.

      Thank you for the tip!

Leave a Reply

Your email address will not be published. Required fields are marked *