Files
HauntedBloodlines/Assets/Editor/FindAndRemoveMissingScripts.cs
2025-05-29 22:31:40 +03:00

234 lines
7.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class MissingScriptUtility : EditorWindow
{
Vector2 scroll;
List<Object> projectObjectsWithMissingScripts = new List<Object>();
int sceneMissingCount = 0;
[MenuItem("Tools/Missing Script Utility")]
static void Init()
{
GetWindow<MissingScriptUtility>("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<GameObject> objectsWithMissing = new List<GameObject>();
GameObject[] go = GameObject.FindObjectsOfType<GameObject>();
foreach (GameObject g in go)
{
Component[] components = g.GetComponents<Component>();
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<Component>(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<GameObject>(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.");
}
}