Hi, I have a feeling that this is a stupid question… but is there a simple function to programmatically get a list of references to an Asset from a given scene (or, even, within a given GameObject)?
maybe its too late but I leave the code here. Works for prefab references and for any component via component context menu
[MenuItem("CONTEXT/Component/Find references to this")]
private static void FindReferences(MenuCommand data)
{
Object context = data.context;
if (context)
{
var comp = context as Component;
if (comp)
FindReferencesTo(comp);
}
}
[MenuItem("Assets/Find references to this")]
private static void FindReferencesToAsset(MenuCommand data)
{
var selected = Selection.activeObject;
if (selected)
FindReferencesTo(selected);
}
private static void FindReferencesTo(Object to)
{
var referencedBy = new List<Object>();
var allObjects = Object.FindObjectsOfType<GameObject>();
for (int j = 0; j < allObjects.Length; j++)
{
var go = allObjects[j];
if (PrefabUtility.GetPrefabType(go) == PrefabType.PrefabInstance)
{
if (PrefabUtility.GetPrefabParent(go) == to)
{
Debug.Log(string.Format("referenced by {0}, {1}", go.name, go.GetType()), go);
referencedBy.Add(go);
}
}
var components = go.GetComponents<Component>();
for (int i = 0; i < components.Length; i++)
{
var c = components*;*
-
if (!c) continue;*
-
var so = new SerializedObject(c);*
-
var sp = so.GetIterator();*
-
while (sp.NextVisible(true))*
-
if (sp.propertyType == SerializedPropertyType.ObjectReference)*
-
{*
-
if (sp.objectReferenceValue == to)*
-
{*
-
Debug.Log(string.Format("referenced by {0}, {1}", c.name, c.GetType()), c);*
-
referencedBy.Add(c.gameObject);*
-
}*
-
}*
-
}*
-
}*
-
if (referencedBy.Any())*
-
Selection.objects = referencedBy.ToArray();*
-
else Debug.Log(“no references in scene”);*
}
Basically the opposite is easy - by calling EditorUtility.CollectReferences you can get a list of all of the things referenced by a game object - to do the inverse is more tricky.
Basically if you loop through every object in your scene and call collect references on it then add the references to a dictionary that references back to the finding object you build a reverse lookup - unfortunately the next complexity is that collecting references on a game object also collects them on the children - which means your best bet is to ensure that you start with the parents and work your way down, removing parent references when a child is added.
So what I do is this:
- Find all of the game objects in the scene using GameObject.FindObjectsOfType(typeof(GameObject))
- Order them by the number of parents they have (in ascending order)
- For each one capture the dependencies using EditorUtility.CollectDependencies
- For each dependency make an entry in a Dictionary<object, List> for the dependency and add the current game object to the list
- For each parent of the current game object - remove it from the List.
At the end of that - given a dependency you have a list of GameObjects that reference it - the only problem is that if it is also explicitly referenced by a parent that parent won’t show up in the list.
Hi @sharsnik ! There is a solution (not the code, but with sources included) - with nice & simple GUI that will:
- show you particular fields where your asset is used
- let you easily replace it
- search for usages of assets that are using your one
You can search for:
- Asset usages in Project
- GameObject usages in Scene
- Component usages in Scene
Asset store link:
Here is improved script of @ToBeGlad (thanks for nice clues, man!). Added several ergonomic enhancements and ability for search for referenced GameObjects only as Components.
Enjoy
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
//using System.Linq;
namespace CarrotOnFire.Assets.Editor
{
public class CarrotFindReferencesHelper : MonoBehaviour
{
[MenuItem(“CONTEXT/Component/Find References to Component”)]
private static void FindReferences(MenuCommand data)
{
Object context = data.context;
if (context)
{
var comp = context as Component;
if (comp)
FindReferencesTo(comp);
}
}
[MenuItem("GameObject/Find References to Selected", false, 0)]
public static void FindReferencesToAsset()
{
var selected = Selection.activeObject;
if (selected)
FindReferencesTo(selected);
}
private static void FindReferencesTo(Object to)
{
var referencedBy = new List<Object>();
var allObjects = Object.FindObjectsOfType<GameObject>();
bool toIsGameObject = to is GameObject;
Component[] toComponents = toIsGameObject ? ((GameObject)to).GetComponents<Component>() : null;
string toName = toIsGameObject ? to.name : string.Format("{0}.{1}", to.name, to.GetType().Name);
for (int j = 0; j < allObjects.Length; j++)
{
GameObject go = allObjects[j];
if (PrefabUtility.GetPrefabType(go) == PrefabType.PrefabInstance)
{
if (PrefabUtility.GetPrefabParent(go) == to)
{
Debug.Log(string.Format("referenced by {0}, {1}", go.name, go.GetType()), go);
referencedBy.Add(go);
}
}
var components = go.GetComponents<Component>();
for (int i = 0; i < components.Length; i++)
{
var component = components*;*
if (!component) continue;
var so = new SerializedObject(component);
var sp = so.GetIterator();
while (sp.NextVisible(true))
{
if (sp.propertyType == SerializedPropertyType.ObjectReference)
{
if (sp.objectReferenceValue == to)
{
Debug.Log(string.Format(“‘{0}’ referenced by ‘{1}’ (Component: ‘{2}’)”, toName, component.name, component.GetType().Name), component);
referencedBy.Add(component.gameObject);
}
else if (toComponents != null)
{
bool found = false;
foreach (Component toComponent in toComponents)
{
if (sp.objectReferenceValue == toComponent)
{
found = true;
referencedBy.Add(component.gameObject);
}
}
if (found)
Debug.Log(string.Format(“‘{0}’ referenced by ‘{1}’ (Component: ‘{2}’)”, toName, component.name, component.GetType().Name), component);
}
}
}
}
}
if (referencedBy.Count > 0)
Selection.objects = referencedBy.ToArray();
else Debug.Log(string.Format(“‘{0}’: no references in scene”, toName));
}
}
}
I would personally try and stay away from iterating the scene objects, this can get expensive quickly. This is a O(n) operation on your selected object. I don’t know if you’re talking about a child object.
Instead implement a design that allows you to store the objects you need in a list and find what you need in that list. Just attach this to each of your objects you need a reference for.
#LogicComponent.cs
List<GameObject> ListOfChildren = new List<GameObject>();
void LogicComponent{
if (transform.parent != null)
transform.parent.GetComponent<LogicComponent>().ListOfChildren .Add(this.GameObject)
}
If you want to have the result IN CODE, I am not sure.
BUT if you want to replicate right click + find references, then read on (it can be a huge timesaver).
When you right click + Find References (or right click + F for faster), unity inputs a search string in the scene hierarchy like this ::
ref:L_FG_Assets_Pack1.0/L_FG_Assets_Pack_Lite/Perfabs/Plant/Plant05_b.prefab
You can replicate that functionality by using a script to manually input text in unity’s filter.
You can use the attached script with the following hotkeys ::
Select a project item and then,
-
Ctrl+F :: Searches the scene for name
-
Alt+Shift+F :: Searches the scene by reference (what the OP wanted)link text
[using System.Reflection;
using UnityEditor;
using UnityEngine;namespace TGP
{
public static class FindReferences
{
[MenuItem(“Tools/Toggle Inspector Lock %f”)] // Ctrl + F
public static void SetSearchFilter_CurrentItem_Name()
{
// Grab the selected item
UnityEngine.Object obj = Selection.activeObject;
if (obj == null) return;SetSearchFilter(obj.name, HierarchyFilterMode.All); } [MenuItem("Tools/Toggle Inspector Lock #&f")] // Ctrl + Shift + F public static void SetSearchFilter_CurrentItem_References() { // Grab the selected item UnityEngine.Object obj = Selection.activeObject; if (obj == null) return; string path = AssetDatabase.GetAssetPath(obj); // Trim the beginning, otherwise it doesn't work path = path.Remove(0, "Assets/".Length); string searchTerm = "ref: " + path; SetSearchFilter(searchTerm, HierarchyFilterMode.All); } // Original from https://stackoverflow.com/questions/29575964/setting-a-hierarchy-filter-via-script, switched to enums public static void SetSearchFilter(string filter, HierarchyFilterMode filterMode) { SearchableEditorWindow[] windows = (SearchableEditorWindow[])Resources.FindObjectsOfTypeAll(typeof(SearchableEditorWindow)); SearchableEditorWindow hierarchy = null; foreach (SearchableEditorWindow window in windows) { if (window.GetType().ToString() == "UnityEditor.SceneHierarchyWindow") { hierarchy = window; break; } } if (hierarchy == null) return; MethodInfo setSearchType = typeof(SearchableEditorWindow).GetMethod("SetSearchFilter", BindingFlags.NonPublic | BindingFlags.Instance); object[] parameters = new object[] { filter, (int)filterMode, false }; setSearchType.Invoke(hierarchy, parameters); } /* public const int FILTERMODE_ALL = 0; public const int FILTERMODE_NAME = 1; public const int FILTERMODE_TYPE = 2; */ public enum HierarchyFilterMode { All = 0, Name = 1, Type = 2 } }
}][2]
There is not a simple funciton to find all references to an asset, but there is a tool which can do this.
Check it out here: Unity Asset Store - The Best Assets for Game Making