Skip to content

Instantly share code, notes, and snippets.

@ArieLeo
Last active August 9, 2022 09:30
Show Gist options
  • Save ArieLeo/c812b06329dbdc0acef9b7e074b6586d to your computer and use it in GitHub Desktop.
Save ArieLeo/c812b06329dbdc0acef9b7e074b6586d to your computer and use it in GitHub Desktop.
Find Missing script recursively on selected Objects and remove them, compatible with the new prefab workflow (nested prefab) tested on Unity 2019.2+
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
public static class FindMissingScriptsRecursively
{
[MenuItem("Auto/Remove Missing Scripts Recursively Visit Prefabs")]
private static void FindAndRemoveMissingInSelected()
{
// EditorUtility.CollectDeepHierarchy does not include inactive children
var deeperSelection = Selection.gameObjects.SelectMany(go => go.GetComponentsInChildren<Transform>(true))
.Select(t => t.gameObject);
var prefabs = new HashSet<Object>();
int compCount = 0;
int goCount = 0;
foreach (var go in deeperSelection)
{
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);
if (count > 0)
{
if (PrefabUtility.IsPartOfAnyPrefab(go))
{
RecursivePrefabSource(go, prefabs, ref compCount, ref goCount);
count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);
// if count == 0 the missing scripts has been removed from prefabs
if (count == 0)
continue;
// if not the missing scripts must be prefab overrides on this instance
}
Undo.RegisterCompleteObjectUndo(go, "Remove missing scripts");
GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go);
compCount += count;
goCount++;
}
}
Debug.Log($"Found and removed {compCount} missing scripts from {goCount} GameObjects");
}
// Prefabs can both be nested or variants, so best way to clean all is to go through them all
// rather than jumping straight to the original prefab source.
private static void RecursivePrefabSource(GameObject instance, HashSet<Object> prefabs, ref int compCount,
ref int goCount)
{
var source = PrefabUtility.GetCorrespondingObjectFromSource(instance);
// Only visit if source is valid, and hasn't been visited before
if (source == null || !prefabs.Add(source))
return;
// go deep before removing, to differantiate local overrides from missing in source
RecursivePrefabSource(source, prefabs, ref compCount, ref goCount);
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(source);
if (count > 0)
{
Undo.RegisterCompleteObjectUndo(source, "Remove missing scripts");
GameObjectUtility.RemoveMonoBehavioursWithMissingScript(source);
compCount += count;
goCount++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment