234 lines
7.2 KiB
C#
234 lines
7.2 KiB
C#
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.");
|
||
}
|
||
}
|