Skip to content

Instantly share code, notes, and snippets.

@marcgpuig
Last active April 16, 2018 15:37
Show Gist options
  • Save marcgpuig/86c76004c3ec6f12d21a246025f0d43c to your computer and use it in GitHub Desktop.
Save marcgpuig/86c76004c3ec6f12d21a246025f0d43c to your computer and use it in GitHub Desktop.
Basic Unity script for searching missing scripts
/// Copyright (c) 2017, Marc Garcia Puig
///
/// This work is licensed under the terms of the MIT license.
/// For a copy, see <https://opensource.org/licenses/MIT>.
///
/// This script provides you the ability to check all your
/// prefabs in the Assets folder and see if any of them have
/// missing scripts. You can spawn them with a single click
/// and clear all the containing missing scripts.
/// Remember to apply the changes to each prefab.
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class ScriptFixer : EditorWindow
{
public Dictionary<string, GameObject> output = new Dictionary<string, GameObject>();
public Dictionary<string, GameObject> spawned = new Dictionary<string, GameObject>();
public string contains = "";
public bool autoSearch = false;
public bool showOnlyMissing = true;
private string extension = "prefab";
private Vector2 outputScroll;
[MenuItem("Synthia/ScriptFixer")]
static void ShowWindow()
{
ScriptFixer window = (ScriptFixer)EditorWindow.GetWindow(typeof(ScriptFixer));
window.Show();
}
/// <summary>
/// Check if a GameObject have a missing component.
/// </summary>
/// <param name="go">The GameObject that we want to analyse</param>
/// <returns></returns>
private bool HasMissingComponent(GameObject go)
{
if (go == null) return false;
Component[] com = go.GetComponentsInChildren<Component>();
foreach (Component c in com) if (c == null) return true;
return false;
}
/// <summary>
/// Search for all prefabs inside the Assets folder.
/// </summary>
private void Search()
{
output.Clear();
string[] assets = AssetDatabase.GetAllAssetPaths();
foreach (string asset in assets)
{
if (asset.Contains(".") && asset.Split('.')[0].Contains(contains))
{
if (extension == "" || asset.Split('.')[1].Contains(extension))
{
output.Add(asset, (GameObject)AssetDatabase.LoadAssetAtPath(asset, typeof(GameObject)));
}
}
}
}
/// <summary>
/// Deletes all the missing scripts from the given game objects.
/// Thanks to https://answers.unity.com/answers/863155/view.html
/// </summary>
/// <param name="objects">List of GameObjects that we want to clean</param>
static void CleanupMissingScripts(GameObject[] objects)
{
for (int i = 0; i < objects.Length; i++)
{
// We must use the GetComponents array to actually detect missing components
Component[] components = objects[i].GetComponents<Component>();
// Create a serialized object so that we can edit the component list
SerializedObject serializedObject = new SerializedObject(objects[i]);
// Find the component list property
SerializedProperty prop = serializedObject.FindProperty("m_Component");
// Track how many components we've removed
int r = 0;
// Iterate over all components
for (int j = 0; j < components.Length; j++)
{
// Check if the ref is null
if (components[j] == null)
{
// If so, remove from the serialized component array
prop.DeleteArrayElementAtIndex(j - r);
// Increment removed count
r++;
}
}
// Apply our changes to the game object
serializedObject.ApplyModifiedProperties();
}
}
private void OnGUI()
{
EditorGUIUtility.labelWidth = 70;
GUILayout.Space(5);
contains = EditorGUILayout.TextField("Contains:", contains);
EditorGUILayout.BeginHorizontal();
{
EditorGUIUtility.labelWidth = 80;
autoSearch = EditorGUILayout.Toggle("AutoSearch", autoSearch);
EditorGUIUtility.labelWidth = 120;
GUILayout.FlexibleSpace();
showOnlyMissing = EditorGUILayout.Toggle("Show Only Missing", showOnlyMissing);
EditorGUIUtility.labelWidth = 70;
}
EditorGUILayout.EndHorizontal();
if (!autoSearch)
GUILayout.Space(10);
// Search
if (!autoSearch && GUILayout.Button("Search", GUILayout.Height(20)) || (autoSearch && GUI.changed))
{
Search();
}
// Remove all spawned GameObjects
if (GUILayout.Button("Remove all spawned GameObjects", GUILayout.Height(20)))
{
foreach (KeyValuePair<string, GameObject> s in spawned)
{
DestroyImmediate(s.Value);
}
spawned.Clear();
}
GUILayout.Space(10);
EditorGUILayout.BeginVertical(GUI.skin.box);
{
// Object List
outputScroll = EditorGUILayout.BeginScrollView(outputScroll);
{
foreach (KeyValuePair<string, GameObject> s in output)
{
Color oldColor = GUI.color;
bool lostComp = HasMissingComponent(s.Value);
if (!lostComp)
{
if (showOnlyMissing) continue;
}
else
{
GUI.color = Color.red;
}
EditorGUILayout.BeginHorizontal(GUI.skin.box);
{
GUI.color = oldColor;
if (!spawned.ContainsKey(s.Key))
{
if (GUILayout.Button("+", GUILayout.Width(19)))
{
if (s.Value.GetComponent<Transform>())
{
spawned.Add(s.Key, (GameObject)(Selection.activeObject = PrefabUtility.InstantiatePrefab(s.Value)));
}
}
}
else
{
GUI.color = new Color(0.75f,0.75f,1f);
if (GUILayout.Button("-", GUILayout.Width(19)))
{
if (s.Value.GetComponent<Transform>())
{
GameObject.DestroyImmediate(spawned[s.Key]);
spawned.Remove(s.Key);
}
}
GUI.color = oldColor;
if (lostComp && GUILayout.Button("Clear Missing Components", GUILayout.Width(170)))
{
GameObject[] objs = new GameObject[spawned[s.Key].transform.childCount + 1];
int i = 0;
foreach (Transform child in spawned[s.Key].transform)
{
objs[i] = child.gameObject;
i++;
}
objs[i] = spawned[s.Key];
CleanupMissingScripts(objs);
}
}
EditorGUILayout.LabelField(new GUIContent(s.Key.Split('/')[s.Key.Split('/').Length - 1], s.Key));
}
EditorGUILayout.EndHorizontal();
}
GUILayout.FlexibleSpace();
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.EndVertical();
EditorGUILayout.LabelField(output.Count.ToString() + " found.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment