I want to delete a script that I think isn’t being used anymore. Is there a way I can get a list of every prefab in the project? I just want to run through all the prefabs and check if a certain script is attached.
Another thing, is there a way to search all prefabs for missing MonoBehaviours? If I delete a script that is attached to some prefab, the prefab then gets a missing MonoBehaviour. Is there a way I can find all those prefabs in the project?
Hey, I was bored, so I made this.
See below for updated version, or expand for this older code
//Assets/Editor/SearchForComponents.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class SearchForComponents : EditorWindow {
[MenuItem( "EDITORS/Search For Components" )]
static void Init () {
SearchForComponents window = (SearchForComponents) EditorWindow.GetWindow( typeof( SearchForComponents ) );
window.Show();
window.position = new Rect( 20, 80, 400, 300 );
}
string[] modes = new string[] { "Search for component usage", "Search for missing components" };
List<string> listResult;
int editorMode, editorModeOld;
MonoScript targetComponent, lastChecked;
string componentName = "";
Vector2 scroll;
void OnGUI () {
GUILayout.Space( 3 );
int oldValue = GUI.skin.window.padding.bottom;
GUI.skin.window.padding.bottom = -20;
Rect windowRect = GUILayoutUtility.GetRect( 1, 17 );
windowRect.x += 4;
windowRect.width -= 7;
editorMode = GUI.SelectionGrid( windowRect, editorMode, modes, 2, "Window" );
GUI.skin.window.padding.bottom = oldValue;
if ( editorModeOld != editorMode ) {
editorModeOld = editorMode;
listResult = new List<string>();
componentName = targetComponent == null ? "" : targetComponent.name;
lastChecked = null;
}
switch ( editorMode ) {
case 0:
targetComponent = (MonoScript) EditorGUILayout.ObjectField( targetComponent, typeof( MonoScript ), false );
if ( targetComponent != lastChecked ) {
lastChecked = targetComponent;
componentName = targetComponent.name;
AssetDatabase.SaveAssets();
string targetPath = AssetDatabase.GetAssetPath( targetComponent );
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs ) {
string[] single = new string[] { prefab };
string[] dependencies = AssetDatabase.GetDependencies( single );
foreach ( string dependedAsset in dependencies ) {
if ( dependedAsset == targetPath ) {
listResult.Add( prefab );
}
}
}
}
break;
case 1:
if ( GUILayout.Button( "Search!" ) ) {
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs ) {
UnityEngine.Object o = AssetDatabase.LoadMainAssetAtPath( prefab );
GameObject go;
try {
go = (GameObject) o;
Component[] components = go.GetComponentsInChildren<Component>( true );
foreach ( Component c in components ) {
if ( c == null ) {
listResult.Add( prefab );
}
}
} catch {
Debug.Log( "For some reason, prefab " + prefab + " won't cast to GameObject" );
}
}
}
break;
}
if ( listResult != null ) {
if ( listResult.Count == 0 ) {
GUILayout.Label( editorMode == 0 ? ( componentName == "" ? "Choose a component" : "No prefabs use component " + componentName ) : ( "No prefabs have missing components!\nClick Search to check again" ) );
} else {
GUILayout.Label( editorMode == 0 ? ( "The following prefabs use component " + componentName + ":" ) : ( "The following prefabs have missing components:" ) );
scroll = GUILayout.BeginScrollView( scroll );
foreach ( string s in listResult ) {
GUILayout.BeginHorizontal();
GUILayout.Label( s, GUILayout.Width( position.width / 2 ) );
if ( GUILayout.Button( "Select", GUILayout.Width( position.width / 2 - 10 ) ) ) {
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath( s );
}
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
}
}
}
public static string[] GetAllPrefabs () {
string[] temp = AssetDatabase.GetAllAssetPaths();
List<string> result = new List<string>();
foreach ( string s in temp ) {
if ( s.Contains( ".prefab" ) ) result.Add( s );
}
return result.ToArray();
}
}
Enjoy!
Hey hpjohn, I just wanted to say I appreciate the script! I haven’t had a chance to go through it line-by-line yet, but some preliminary testing indicates that it works like a charm.
Our project is like over a thousand scripts and nearing a thousand prefabs at this point. Housekeeping is becoming a pain. Thanks for bringing order to this chaos!
Don’t forget, any scripts that are actually used in other scripts via AddComponent wont get picked up by this.
Automation is key to this. That, and being organised up front.
Learning how to write scripts for the Editor will boost your productivity by orders of magnitude in some cases. The other day I spend 45 seconds to write a script which automated a task people had been working on for around 15 minutes. The script got it done in one shot in under a second. While it wasn’t relevant in that case, using a script to make bulk changes or whatnot also removes the possibility of human error and ensures consistency.
I was thinking last night, you probably wouldn’t want to check 1000 scripts one-by-one, so I put an option for checking all scripts in a project…
then I was thinking about ways to find AddComponent(xxx) occurances…
and then I was thinking about how to detect components in scenes that aren’t prefabs
…and, well…
it just got out of hand.
//Assets/Editor/SearchForComponents.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class SearchForComponents : EditorWindow {
[MenuItem( "EDITORS/Search For Components" )]
static void Init () {
SearchForComponents window = (SearchForComponents) EditorWindow.GetWindow( typeof( SearchForComponents ) );
window.Show();
window.position = new Rect( 20, 80, 550, 500 );
}
string[] modes = new string[] { "Search for component usage", "Search for missing components" };
string[] checkType = new string[] { "Check single component", "Check all components" };
List<string> listResult;
List<ComponentNames> prefabComponents,notUsedComponents, addedComponents, existingComponents, sceneComponents;
int editorMode, selectedCheckType;
MonoScript targetComponent;
string componentName = "";
bool showPrefabs, showAdded, showScene, showUnused = true;
Vector2 scroll, scroll1, scroll2, scroll3, scroll4;
class ComponentNames {
public string componentName;
public string namespaceName;
public string assetPath;
public List<string> usageSource;
public ComponentNames ( string comp, string space, string path ) {
this.componentName = comp;
this.namespaceName = space;
this.assetPath = path;
this.usageSource = new List<string>();
}
public override bool Equals ( object obj ) {
return ( (ComponentNames) obj ).componentName == componentName && ( (ComponentNames) obj ).namespaceName == namespaceName;
}
public override int GetHashCode () {
return componentName.GetHashCode() + namespaceName.GetHashCode();
}
}
void OnGUI () {
GUILayout.Label(position+"");
GUILayout.Space( 3 );
int oldValue = GUI.skin.window.padding.bottom;
GUI.skin.window.padding.bottom = -20;
Rect windowRect = GUILayoutUtility.GetRect( 1, 17 );
windowRect.x += 4;
windowRect.width -= 7;
editorMode = GUI.SelectionGrid( windowRect, editorMode, modes, 2, "Window" );
GUI.skin.window.padding.bottom = oldValue;
switch ( editorMode ) {
case 0:
selectedCheckType = GUILayout.SelectionGrid( selectedCheckType, checkType, 2, "Toggle" );
GUI.enabled = selectedCheckType == 0;
targetComponent = (MonoScript) EditorGUILayout.ObjectField( targetComponent, typeof( MonoScript ), false );
GUI.enabled = true;
if ( GUILayout.Button( "Check component usage" ) ) {
AssetDatabase.SaveAssets();
switch ( selectedCheckType ) {
case 0:
componentName = targetComponent.name;
string targetPath = AssetDatabase.GetAssetPath( targetComponent );
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs ) {
string[] single = new string[] { prefab };
string[] dependencies = AssetDatabase.GetDependencies( single );
foreach ( string dependedAsset in dependencies ) {
if ( dependedAsset == targetPath ) {
listResult.Add( prefab );
}
}
}
break;
case 1:
List<string> scenesToLoad = new List<string>();
existingComponents = new List<ComponentNames>();
prefabComponents = new List<ComponentNames>();
notUsedComponents = new List<ComponentNames>();
addedComponents = new List<ComponentNames>();
sceneComponents = new List<ComponentNames>();
if ( EditorApplication.SaveCurrentSceneIfUserWantsTo() ) {
string projectPath = Application.dataPath;
projectPath = projectPath.Substring( 0, projectPath.IndexOf( "Assets" ) );
string[] allAssets = AssetDatabase.GetAllAssetPaths();
foreach ( string asset in allAssets ) {
int indexCS = asset.IndexOf( ".cs" );
int indexJS = asset.IndexOf( ".js" );
if ( indexCS != -1 || indexJS != -1 ) {
ComponentNames newComponent = new ComponentNames( NameFromPath( asset ), "", asset );
try {
System.IO.FileStream FS = new System.IO.FileStream( projectPath + asset, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite );
System.IO.StreamReader SR = new System.IO.StreamReader( FS );
string line;
while ( !SR.EndOfStream ) {
line = SR.ReadLine();
int index1 = line.IndexOf( "namespace" );
int index2 = line.IndexOf( "{" );
if ( index1 != -1 && index2 != -1 ) {
line = line.Substring( index1 + 9 );
index2 = line.IndexOf( "{" );
line = line.Substring( 0, index2 );
line = line.Replace( " ", "" );
newComponent.namespaceName = line;
}
}
} catch {
}
existingComponents.Add( newComponent );
try {
System.IO.FileStream FS = new System.IO.FileStream( projectPath + asset, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite );
System.IO.StreamReader SR = new System.IO.StreamReader( FS );
string line;
int lineNum = 0;
while ( !SR.EndOfStream ) {
lineNum++;
line = SR.ReadLine();
int index = line.IndexOf( "AddComponent" );
if ( index != -1 ) {
line = line.Substring( index + 12 );
if ( line[0] == '(' ) {
line = line.Substring( 1, line.IndexOf( ')' ) - 1 );
} else if ( line[0] == '<' ) {
line = line.Substring( 1, line.IndexOf( '>' ) - 1 );
} else {
continue;
}
line = line.Replace( " ", "" );
line = line.Replace( "\"", "" );
index = line.LastIndexOf( '.' );
ComponentNames newComp;
if ( index == -1 ) {
newComp = new ComponentNames( line, "", "" );
} else {
newComp = new ComponentNames( line.Substring( index + 1, line.Length - ( index + 1 ) ), line.Substring( 0, index ), "" );
}
string pName = asset + ", Line " + lineNum;
newComp.usageSource.Add( pName );
index = addedComponents.IndexOf( newComp );
if ( index == -1 ) {
addedComponents.Add( newComp );
} else {
if ( !addedComponents[index].usageSource.Contains( pName ) ) addedComponents[index].usageSource.Add( pName );
}
}
}
} catch {
}
}
int indexPrefab = asset.IndexOf( ".prefab" );
if ( indexPrefab != -1 ) {
string[] single = new string[] { asset };
string[] dependencies = AssetDatabase.GetDependencies( single );
foreach ( string dependedAsset in dependencies ) {
if ( dependedAsset.IndexOf( ".cs" ) != -1 || dependedAsset.IndexOf( ".js" ) != -1 ) {
ComponentNames newComponent = new ComponentNames( NameFromPath( dependedAsset ), GetNamespaceFromPath( dependedAsset ), dependedAsset );
int index = prefabComponents.IndexOf( newComponent );
if ( index == -1 ) {
newComponent.usageSource.Add( asset );
prefabComponents.Add( newComponent );
} else {
if ( !prefabComponents[index].usageSource.Contains( asset ) ) prefabComponents[index].usageSource.Add( asset );
}
}
}
}
int indexUnity = asset.IndexOf( ".unity" );
if ( indexUnity != -1 ) {
scenesToLoad.Add( asset );
}
}
for ( int i = addedComponents.Count - 1; i > -1; i-- ) {
addedComponents[i].assetPath = GetPathFromNames( addedComponents[i].namespaceName, addedComponents[i].componentName );
if ( addedComponents[i].assetPath == "" ) addedComponents.RemoveAt( i );
}
foreach ( string scene in scenesToLoad ) {
EditorApplication.OpenScene( scene );
GameObject[] sceneGOs = GetAllObjectsInScene();
foreach ( GameObject g in sceneGOs ) {
Component[] comps = g.GetComponentsInChildren<Component>( true );
foreach ( Component c in comps ) {
if ( c != null && c.GetType() != null && c.GetType().BaseType != null && c.GetType().BaseType == typeof( MonoBehaviour ) ) {
SerializedObject so = new SerializedObject( c );
SerializedProperty p = so.FindProperty( "m_Script" );
string path = AssetDatabase.GetAssetPath( p.objectReferenceValue );
ComponentNames newComp = new ComponentNames( NameFromPath( path ), GetNamespaceFromPath( path ), path );
newComp.usageSource.Add( scene );
int index = sceneComponents.IndexOf( newComp );
if ( index == -1 ) {
sceneComponents.Add( newComp );
} else {
if ( !sceneComponents[index].usageSource.Contains( scene ) ) sceneComponents[index].usageSource.Add( scene );
}
}
}
}
}
foreach ( ComponentNames c in existingComponents ) {
if ( addedComponents.Contains( c ) ) continue;
if ( prefabComponents.Contains( c ) ) continue;
if ( sceneComponents.Contains( c ) ) continue;
notUsedComponents.Add( c );
}
addedComponents.Sort( SortAlphabetically );
prefabComponents.Sort( SortAlphabetically );
sceneComponents.Sort( SortAlphabetically );
notUsedComponents.Sort( SortAlphabetically );
}
break;
}
}
break;
case 1:
if ( GUILayout.Button( "Search!" ) ) {
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs ) {
UnityEngine.Object o = AssetDatabase.LoadMainAssetAtPath( prefab );
GameObject go;
try {
go = (GameObject) o;
Component[] components = go.GetComponentsInChildren<Component>( true );
foreach ( Component c in components ) {
if ( c == null ) {
listResult.Add( prefab );
}
}
} catch {
Debug.Log( "For some reason, prefab " + prefab + " won't cast to GameObject" );
}
}
}
break;
}
if ( editorMode == 1 || selectedCheckType == 0 ) {
if ( listResult != null ) {
if ( listResult.Count == 0 ) {
GUILayout.Label( editorMode == 0 ? ( componentName == "" ? "Choose a component" : "No prefabs use component " + componentName ) : ( "No prefabs have missing components!\nClick Search to check again" ) );
} else {
GUILayout.Label( editorMode == 0 ? ( "The following prefabs use component " + componentName + ":" ) : ( "The following prefabs have missing components:" ) );
scroll = GUILayout.BeginScrollView( scroll );
foreach ( string s in listResult ) {
GUILayout.BeginHorizontal();
GUILayout.Label( s, GUILayout.Width( position.width / 2 ) );
if ( GUILayout.Button( "Select", GUILayout.Width( position.width / 2 - 10 ) ) ) {
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath( s );
}
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
}
}
} else {
showPrefabs = GUILayout.Toggle( showPrefabs, "Show prefab components" );
if ( showPrefabs ) {
GUILayout.Label( "The following components are attatched to prefabs:" );
DisplayResults( ref scroll1, ref prefabComponents );
}
showAdded = GUILayout.Toggle( showAdded, "Show AddComponent arguments" );
if ( showAdded ) {
GUILayout.Label( "The following components are AddComponent arguments:" );
DisplayResults( ref scroll2, ref addedComponents );
}
showScene = GUILayout.Toggle( showScene, "Show Scene-used components" );
if ( showScene ) {
GUILayout.Label( "The following components are used by scene objects:" );
DisplayResults( ref scroll3, ref sceneComponents );
}
showUnused = GUILayout.Toggle( showUnused, "Show Unused Components" );
if ( showUnused ) {
GUILayout.Label( "The following components are not used by prefabs, by AddComponent, OR in any scene:" );
DisplayResults( ref scroll4, ref notUsedComponents );
}
}
}
int SortAlphabetically ( ComponentNames a, ComponentNames b ) {
return a.assetPath.CompareTo( b.assetPath );
}
GameObject[] GetAllObjectsInScene () {
List<GameObject> objectsInScene = new List<GameObject>();
GameObject[] allGOs = (GameObject[]) Resources.FindObjectsOfTypeAll( typeof( GameObject ) );
foreach ( GameObject go in allGOs ) {
//if ( go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave )
// continue;
string assetPath = AssetDatabase.GetAssetPath( go.transform.root.gameObject );
if ( !string.IsNullOrEmpty( assetPath ) )
continue;
objectsInScene.Add( go );
}
return objectsInScene.ToArray();
}
void DisplayResults ( ref Vector2 scroller, ref List<ComponentNames> list ) {
if ( list == null ) return;
scroller = GUILayout.BeginScrollView( scroller );
foreach ( ComponentNames c in list ) {
GUILayout.BeginHorizontal();
GUILayout.Label( c.assetPath, GUILayout.Width( position.width / 5 *4 ) );
if ( GUILayout.Button( "Select", GUILayout.Width( position.width / 5 - 30 ) ) ) {
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath( c.assetPath );
}
GUILayout.EndHorizontal();
if ( c.usageSource.Count == 1 ) {
GUILayout.Label( " In 1 Place: " + c.usageSource[0] );
}
if ( c.usageSource.Count > 1 ) {
GUILayout.Label( " In " + c.usageSource.Count + " Places: " + c.usageSource[0] + ", " + c.usageSource[1] + ( c.usageSource.Count > 2 ? ", ..." : "" ) );
}
}
GUILayout.EndScrollView();
}
string NameFromPath ( string s ) {
s = s.Substring( s.LastIndexOf( '/' ) + 1 );
return s.Substring( 0, s.Length - 3 );
}
string GetNamespaceFromPath ( string path ) {
foreach ( ComponentNames c in existingComponents ) {
if ( c.assetPath == path ) {
return c.namespaceName;
}
}
return "";
}
string GetPathFromNames ( string space, string name ) {
ComponentNames test = new ComponentNames( name, space, "" );
int index = existingComponents.IndexOf( test );
if ( index != -1 ) {
return existingComponents[index].assetPath;
}
return "";
}
public static string[] GetAllPrefabs () {
string[] temp = AssetDatabase.GetAllAssetPaths();
List<string> result = new List<string>();
foreach ( string s in temp ) {
if ( s.Contains( ".prefab" ) ) result.Add( s );
}
return result.ToArray();
}
}
Theres a still a couple of ways a dependancy can slip through, if you have the following it wont catch it:
string componentName = "MyComponent";
gameObject.AddComponent( componentName );
And any scripts that arent really MonoBehaviours, eg scriptable objects or just classes; these will be picked up and listed as ‘Unused’.
I’m also not entirely sure how namespaces will affect it, but what I tested worked fine. (Win7 machine)
Be aware, when you use the new option, it opens all the scenes in your prjoect to check the objects in them, so if you’ve got a lot of/large scenes, it might take a while to grind through them, but there’s no infinate loops, so it’s not crashing out, just give it a sec.
@hpjohn Cool! I’ll give this a go later today and let you know if I find any bugs. I feel like I can’t be the first person dealing with a messy project and some other people might find this useful. Mind if I throw it on github or something?
@angrypenguin You say automation is key, and that’s one of the reasons we have so many prefabs now! lol I’m looking at 2D Toolkit and it seems to keep track of all it’s own assets all the time. Whereas my editor code is being used to make-and-forget prefabs. We automated prefab creation, but we didn’t automate clean-up! >.>
wow this is almost a year old… but I just came here to say thank you for the script haha, very useful!
Nice…thank you…was about to hook up localisations to spawned in game prefabs… and I used this to find all my text prefabs…my hair will be eternally grateful!
I just discovered this script and found it very helpful for the project I’m working on. I made one small addition to hpjohn’s script which gives you the option to toggle between searching for the component/script in all dependencies or just direct dependencies.
Thanks!
//Assets/Editor/SearchForComponents.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class SearchForComponents : EditorWindow
{
[MenuItem( "Deadly Soap Tools/Search For Components" )]
static void Init ()
{
SearchForComponents window = (SearchForComponents) EditorWindow.GetWindow( typeof( SearchForComponents ) );
window.Show();
window.position = new Rect( 20, 80, 550, 500 );
}
string[] modes = new string[] { "Search for component usage", "Search for missing components" };
string[] checkType = new string[] { "Check single component", "Check all components" };
List<string> listResult;
List<ComponentNames> prefabComponents,notUsedComponents, addedComponents, existingComponents, sceneComponents;
int editorMode, selectedCheckType;
bool recursionVal;
MonoScript targetComponent;
string componentName = "";
bool showPrefabs, showAdded, showScene, showUnused = true;
Vector2 scroll, scroll1, scroll2, scroll3, scroll4;
class ComponentNames
{
public string componentName;
public string namespaceName;
public string assetPath;
public List<string> usageSource;
public ComponentNames ( string comp, string space, string path )
{
this.componentName = comp;
this.namespaceName = space;
this.assetPath = path;
this.usageSource = new List<string>();
}
public override bool Equals ( object obj )
{
return ( (ComponentNames) obj ).componentName == componentName && ( (ComponentNames) obj ).namespaceName == namespaceName;
}
public override int GetHashCode ()
{
return componentName.GetHashCode() + namespaceName.GetHashCode();
}
}
void OnGUI ()
{
GUILayout.Label(position+"");
GUILayout.Space( 3 );
int oldValue = GUI.skin.window.padding.bottom;
GUI.skin.window.padding.bottom = -20;
Rect windowRect = GUILayoutUtility.GetRect( 1, 17 );
windowRect.x += 4;
windowRect.width -= 7;
editorMode = GUI.SelectionGrid( windowRect, editorMode, modes, 2, "Window" );
GUI.skin.window.padding.bottom = oldValue;
switch ( editorMode )
{
case 0:
selectedCheckType = GUILayout.SelectionGrid (selectedCheckType, checkType, 2, "Toggle");
recursionVal = GUILayout.Toggle (recursionVal, "Search all dependencies");
GUI.enabled = selectedCheckType == 0;
targetComponent = (MonoScript) EditorGUILayout.ObjectField( targetComponent, typeof( MonoScript ), false );
GUI.enabled = true;
if ( GUILayout.Button( "Check component usage" ) )
{
AssetDatabase.SaveAssets();
switch ( selectedCheckType )
{
case 0:
componentName = targetComponent.name;
string targetPath = AssetDatabase.GetAssetPath( targetComponent );
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs )
{
string[] single = new string[] { prefab };
string[] dependencies = AssetDatabase.GetDependencies( single, recursionVal );
foreach ( string dependedAsset in dependencies )
{
if ( dependedAsset == targetPath )
{
listResult.Add( prefab );
}
}
}
break;
case 1:
List<string> scenesToLoad = new List<string>();
existingComponents = new List<ComponentNames>();
prefabComponents = new List<ComponentNames>();
notUsedComponents = new List<ComponentNames>();
addedComponents = new List<ComponentNames>();
sceneComponents = new List<ComponentNames>();
if ( EditorApplication.SaveCurrentSceneIfUserWantsTo() )
{
string projectPath = Application.dataPath;
projectPath = projectPath.Substring( 0, projectPath.IndexOf( "Assets" ) );
string[] allAssets = AssetDatabase.GetAllAssetPaths();
foreach ( string asset in allAssets )
{
int indexCS = asset.IndexOf( ".cs" );
int indexJS = asset.IndexOf( ".js" );
if ( indexCS != -1 || indexJS != -1 )
{
ComponentNames newComponent = new ComponentNames( NameFromPath( asset ), "", asset );
try
{
System.IO.FileStream FS = new System.IO.FileStream( projectPath + asset, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite );
System.IO.StreamReader SR = new System.IO.StreamReader( FS );
string line;
while ( !SR.EndOfStream )
{
line = SR.ReadLine();
int index1 = line.IndexOf( "namespace" );
int index2 = line.IndexOf( "{" );
if ( index1 != -1 && index2 != -1 )
{
line = line.Substring( index1 + 9 );
index2 = line.IndexOf( "{" );
line = line.Substring( 0, index2 );
line = line.Replace( " ", "" );
newComponent.namespaceName = line;
}
}
}
catch
{
}
existingComponents.Add( newComponent );
try
{
System.IO.FileStream FS = new System.IO.FileStream( projectPath + asset, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite );
System.IO.StreamReader SR = new System.IO.StreamReader( FS );
string line;
int lineNum = 0;
while ( !SR.EndOfStream )
{
lineNum++;
line = SR.ReadLine();
int index = line.IndexOf( "AddComponent" );
if ( index != -1 )
{
line = line.Substring( index + 12 );
if ( line[0] == '(' )
{
line = line.Substring( 1, line.IndexOf( ')' ) - 1 );
}
else if ( line[0] == '<' )
{
line = line.Substring( 1, line.IndexOf( '>' ) - 1 );
}
else
{
continue;
}
line = line.Replace( " ", "" );
line = line.Replace( "\"", "" );
index = line.LastIndexOf( '.' );
ComponentNames newComp;
if ( index == -1 )
{
newComp = new ComponentNames( line, "", "" );
}
else
{
newComp = new ComponentNames( line.Substring( index + 1, line.Length - ( index + 1 ) ), line.Substring( 0, index ), "" );
}
string pName = asset + ", Line " + lineNum;
newComp.usageSource.Add( pName );
index = addedComponents.IndexOf( newComp );
if ( index == -1 )
{
addedComponents.Add( newComp );
}
else
{
if ( !addedComponents[index].usageSource.Contains( pName ) ) addedComponents[index].usageSource.Add( pName );
}
}
}
}
catch
{
}
}
int indexPrefab = asset.IndexOf( ".prefab" );
if ( indexPrefab != -1 )
{
string[] single = new string[] { asset };
string[] dependencies = AssetDatabase.GetDependencies( single, recursionVal );
foreach ( string dependedAsset in dependencies )
{
if ( dependedAsset.IndexOf( ".cs" ) != -1 || dependedAsset.IndexOf( ".js" ) != -1 )
{
ComponentNames newComponent = new ComponentNames( NameFromPath( dependedAsset ), GetNamespaceFromPath( dependedAsset ), dependedAsset );
int index = prefabComponents.IndexOf( newComponent );
if ( index == -1 )
{
newComponent.usageSource.Add( asset );
prefabComponents.Add( newComponent );
}
else
{
if ( !prefabComponents[index].usageSource.Contains( asset ) ) prefabComponents[index].usageSource.Add( asset );
}
}
}
}
int indexUnity = asset.IndexOf( ".unity" );
if ( indexUnity != -1 )
{
scenesToLoad.Add( asset );
}
}
for ( int i = addedComponents.Count - 1; i > -1; i-- )
{
addedComponents[i].assetPath = GetPathFromNames( addedComponents[i].namespaceName, addedComponents[i].componentName );
if ( addedComponents[i].assetPath == "" ) addedComponents.RemoveAt( i );
}
foreach ( string scene in scenesToLoad )
{
EditorApplication.OpenScene( scene );
GameObject[] sceneGOs = GetAllObjectsInScene();
foreach ( GameObject g in sceneGOs )
{
Component[] comps = g.GetComponentsInChildren<Component>( true );
foreach ( Component c in comps )
{
if ( c != null && c.GetType() != null && c.GetType().BaseType != null && c.GetType().BaseType == typeof( MonoBehaviour ) )
{
SerializedObject so = new SerializedObject( c );
SerializedProperty p = so.FindProperty( "m_Script" );
string path = AssetDatabase.GetAssetPath( p.objectReferenceValue );
ComponentNames newComp = new ComponentNames( NameFromPath( path ), GetNamespaceFromPath( path ), path );
newComp.usageSource.Add( scene );
int index = sceneComponents.IndexOf( newComp );
if ( index == -1 )
{
sceneComponents.Add( newComp );
}
else
{
if ( !sceneComponents[index].usageSource.Contains( scene ) ) sceneComponents[index].usageSource.Add( scene );
}
}
}
}
}
foreach ( ComponentNames c in existingComponents )
{
if ( addedComponents.Contains( c ) ) continue;
if ( prefabComponents.Contains( c ) ) continue;
if ( sceneComponents.Contains( c ) ) continue;
notUsedComponents.Add( c );
}
addedComponents.Sort( SortAlphabetically );
prefabComponents.Sort( SortAlphabetically );
sceneComponents.Sort( SortAlphabetically );
notUsedComponents.Sort( SortAlphabetically );
}
break;
}
}
break;
case 1:
if ( GUILayout.Button( "Search!" ) )
{
string[] allPrefabs = GetAllPrefabs();
listResult = new List<string>();
foreach ( string prefab in allPrefabs )
{
UnityEngine.Object o = AssetDatabase.LoadMainAssetAtPath( prefab );
GameObject go;
try
{
go = (GameObject) o;
Component[] components = go.GetComponentsInChildren<Component>( true );
foreach ( Component c in components )
{
if ( c == null )
{
listResult.Add( prefab );
}
}
}
catch
{
Debug.Log( "For some reason, prefab " + prefab + " won't cast to GameObject" );
}
}
}
break;
}
if ( editorMode == 1 || selectedCheckType == 0 )
{
if ( listResult != null )
{
if ( listResult.Count == 0 )
{
GUILayout.Label( editorMode == 0 ? ( componentName == "" ? "Choose a component" : "No prefabs use component " + componentName ) : ( "No prefabs have missing components!\nClick Search to check again" ) );
}
else
{
GUILayout.Label( editorMode == 0 ? ( "The following " + listResult.Count + " prefabs use component " + componentName + ":" ) : ( "The following prefabs have missing components:" ) );
scroll = GUILayout.BeginScrollView( scroll );
foreach ( string s in listResult )
{
GUILayout.BeginHorizontal();
GUILayout.Label( s, GUILayout.Width( position.width / 2 ) );
if ( GUILayout.Button( "Select", GUILayout.Width( position.width / 2 - 10 ) ) )
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath( s );
}
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
}
}
}
else
{
showPrefabs = GUILayout.Toggle( showPrefabs, "Show prefab components" );
if ( showPrefabs )
{
GUILayout.Label( "The following components are attatched to prefabs:" );
DisplayResults( ref scroll1, ref prefabComponents );
}
showAdded = GUILayout.Toggle( showAdded, "Show AddComponent arguments" );
if ( showAdded )
{
GUILayout.Label( "The following components are AddComponent arguments:" );
DisplayResults( ref scroll2, ref addedComponents );
}
showScene = GUILayout.Toggle( showScene, "Show Scene-used components" );
if ( showScene )
{
GUILayout.Label( "The following components are used by scene objects:" );
DisplayResults( ref scroll3, ref sceneComponents );
}
showUnused = GUILayout.Toggle( showUnused, "Show Unused Components" );
if ( showUnused )
{
GUILayout.Label( "The following components are not used by prefabs, by AddComponent, OR in any scene:" );
DisplayResults( ref scroll4, ref notUsedComponents );
}
}
}
int SortAlphabetically ( ComponentNames a, ComponentNames b )
{
return a.assetPath.CompareTo( b.assetPath );
}
GameObject[] GetAllObjectsInScene ()
{
List<GameObject> objectsInScene = new List<GameObject>();
GameObject[] allGOs = (GameObject[]) Resources.FindObjectsOfTypeAll( typeof( GameObject ) );
foreach ( GameObject go in allGOs )
{
//if ( go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave )
// continue;
string assetPath = AssetDatabase.GetAssetPath( go.transform.root.gameObject );
if ( !string.IsNullOrEmpty( assetPath ) )
continue;
objectsInScene.Add( go );
}
return objectsInScene.ToArray();
}
void DisplayResults ( ref Vector2 scroller, ref List<ComponentNames> list )
{
if ( list == null ) return;
scroller = GUILayout.BeginScrollView( scroller );
foreach ( ComponentNames c in list )
{
GUILayout.BeginHorizontal();
GUILayout.Label( c.assetPath, GUILayout.Width( position.width / 5 *4 ) );
if ( GUILayout.Button( "Select", GUILayout.Width( position.width / 5 - 30 ) ) )
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath( c.assetPath );
}
GUILayout.EndHorizontal();
if ( c.usageSource.Count == 1 )
{
GUILayout.Label( " In 1 Place: " + c.usageSource[0] );
}
if ( c.usageSource.Count > 1 )
{
GUILayout.Label( " In " + c.usageSource.Count + " Places: " + c.usageSource[0] + ", " + c.usageSource[1] + ( c.usageSource.Count > 2 ? ", ..." : "" ) );
}
}
GUILayout.EndScrollView();
}
string NameFromPath ( string s )
{
s = s.Substring( s.LastIndexOf( '/' ) + 1 );
return s.Substring( 0, s.Length - 3 );
}
string GetNamespaceFromPath ( string path )
{
foreach ( ComponentNames c in existingComponents )
{
if ( c.assetPath == path )
{
return c.namespaceName;
}
}
return "";
}
string GetPathFromNames ( string space, string name )
{
ComponentNames test = new ComponentNames( name, space, "" );
int index = existingComponents.IndexOf( test );
if ( index != -1 )
{
return existingComponents[index].assetPath;
}
return "";
}
public static string[] GetAllPrefabs ()
{
string[] temp = AssetDatabase.GetAllAssetPaths();
List<string> result = new List<string>();
foreach ( string s in temp )
{
if ( s.Contains( ".prefab" ) ) result.Add( s );
}
return result.ToArray();
}
}
@hpjohn @andrew_ES How do I specify the class for standard component (e.g. mesh filter) instead of monoscript?
Brilliant script. Upgraded it to work with my current unity (5.6.0f3)
Is there a way to add “remove” all found components? I tried to modify it and call DestroyImmediate(component) but that didn’t work
Very helpful that SearchForComponents Script, thanks a lot
There are a lot of great responses for finding missing components, but no one really talked about how to find all MonoBehaviours in your project.
You can find MonoBehaviours on prefabs really easily. Once found, you can do whatever you want with them. You can select them in the editor, make changes to them, etc. I’m not sure if you can remove MonoBehaviours, maybe if you SetDirty(foundScript.gameObject)?
Here’s some example code:
MyScript[] allFoundScripts = Resources.FindObjectsOfTypeAll<MyScript>();
foreach(MyScript foundScript in allFoundScripts)
{
Debug.Log("Found the script in: " + foundScript.gameObject);
// Select the script in the inspector, if you want to
UnityEditor.Selection.activeGameObject = foundScript.gameObject;
// You can also change variables on the found script
foundScript.someVariable = 13;
// Set dirty forces the inspector to save the change (there may be a better way to do this)
UnityEditor.EditorUtility.SetDirty(foundScript);
}
Building on @millershaski 's answer, here’s a simple EditorWindow for finding all prefabs that have the selected MonoBehaviour attached. Choose your MonoBehaviour and it will list all the prefabs that have it attached as buttons. Click the button and the Project View will select that prefab.
using UnityEditor;
using UnityEngine;
public class FindComponentUsagesWindow : EditorWindow
{
private MonoScript targetMonoScript;
private Vector2 scrollPos;
[MenuItem("Tools/Find Component Usages")]
public static void ShowWindow()
{
GetWindow<FindComponentUsagesWindow>(true, "Find Component Usages", true);
}
void OnGUI()
{
targetMonoScript = (MonoScript)EditorGUILayout.ObjectField(targetMonoScript, typeof(MonoScript), false);
if (targetMonoScript != null)
{
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
System.Type targetType = targetMonoScript.GetClass();
if (targetType != null && targetType.IsSubclassOf(typeof(MonoBehaviour)))
{
Object[] allMonoscriptsAsObjects = Resources.FindObjectsOfTypeAll(targetType);
foreach (Object monoscriptAsObject in allMonoscriptsAsObjects)
{
GameObject prefab = ((MonoBehaviour)monoscriptAsObject).gameObject;
if (GUILayout.Button(prefab.name))
{
Selection.activeObject = prefab;
}
}
}
else
{
EditorGUILayout.LabelField($"{targetMonoScript.name} is not a subclass of MonoBehavior");
}
EditorGUILayout.EndScrollView();
}
}
}
Thank you @hpjohn
While we’re at it, a throw-away script that locates all usages of the built-in Sphere mesh in all assets—simple enough to be quickly edited. If you have both the Console and the Project windows visible at the same time, clicking on each result in the Console highlights it in the Project window.
using System.Linq;
using UnityEditor;
using UnityEngine;
public static class FindAllSpheres
{
private const string MENU_ROOT = "Window/Where Are All Spheres";
[MenuItem(MENU_ROOT)]
public static void _FindAllSpheres()
{
var go1 = GameObject.CreatePrimitive(PrimitiveType.Sphere);
var searchMesh = go1.GetComponent<MeshFilter>().sharedMesh;
Object.DestroyImmediate(go1);
string[] pathsToAssets = AssetDatabase.FindAssets("t:GameObject");
Debug.Log("searching in " + pathsToAssets.Length + " gameobjects...");
foreach (var path in pathsToAssets)
{
var path1 = AssetDatabase.GUIDToAssetPath(path);
var go = AssetDatabase.LoadAssetAtPath<GameObject>(path1);
if (go.GetComponentsInChildren<MeshFilter>().Any(mf => mf.sharedMesh == searchMesh))
Debug.LogWarning("FOUND: " + path1, go);
}
}
}
Using DoomGoober’s script above, I’d like to slightly modify it to find components, then on same gameObject… add another specified component.
For example… I have a paramater
MonoScript scrToAdd; //script to add to game object
…and a found ‘prefab’ with my target MonoScript type…
how do I add ‘scrToAdd’ to the prefab please?..
prefab.AddComponent(); //does not work
prefab.AddComponent<typeof(scrToAdd)>(); //does not work
but it works if I just type in a class as normal… e.g.
prefab.AddComponent();
how do I do this with a MonoScript paramater, or how do I cast MonoScript to something AddComponent understands please ?