Provides various utility functions that are used by SearchProvider.
using System; using System.Collections; using System.Globalization; using System.Linq; using UnityEditor.Search; using UnityEditor; using UnityEngine; /// <summary> /// Custom provider showing how to implement a custom Query Engine supporting a Spatial search filter. /// </summary> public static class SpatialProvider { internal static string type = "spl"; internal static string displayName = "Spatial"; static GameObject[] s_GameObjects; static QueryEngine<GameObject> s_QueryEngine; [SearchItemProvider] internal static SearchProvider CreateProvider() { return new SearchProvider(type, displayName) { active = false, filterId = "spl:", onEnable = OnEnable, fetchItems = (context, items, provider) => SearchItems(context, provider), fetchLabel = FetchLabel, fetchDescription = FetchDescription, fetchThumbnail = FetchThumbnail, fetchPreview = FetchPreview, trackSelection = TrackSelection, isExplicitProvider = false, }; } static void OnEnable() { s_GameObjects = SearchUtils.FetchGameObjects().ToArray(); s_QueryEngine = new QueryEngine<GameObject>(); // Id supports all operators s_QueryEngine.AddFilter("id", go => go.GetInstanceID()); // Name supports only :, = and != s_QueryEngine.AddFilter("n", go => go.name, new[] {":", "=", "!="}); // Add distance filtering. Does not support :. s_QueryEngine.AddFilter("dist", DistanceHandler, DistanceParamHandler, new[] {"=", "!=", "<", ">", "<=", ">="}); } static IEnumerator SearchItems(SearchContext context, SearchProvider provider) { var query = s_QueryEngine.Parse(context.searchQuery); if (!query.valid) yield break; var filteredObjects = query.Apply(s_GameObjects); foreach (var filteredObject in filteredObjects) { yield return provider.CreateItem(filteredObject.GetInstanceID().ToString(), null, null, null, filteredObject.GetInstanceID()); } } static string FetchLabel(SearchItem item, SearchContext context) { if (item.label != null) return item.label; var go = ObjectFromItem(item); if (!go) return item.id; var transformPath = SearchUtils.GetTransformPath(go.transform); var components = go.GetComponents<Component>(); if (components.Length > 2 && components[1] && components[components.Length - 1]) item.label = $"{transformPath} ({components[1].GetType().Name}..{components[components.Length - 1].GetType().Name})"; else if (components.Length > 1 && components[1]) item.label = $"{transformPath} ({components[1].GetType().Name})"; else item.label = $"{transformPath} ({item.id})"; return item.label; } static string FetchDescription(SearchItem item, SearchContext context) { var go = ObjectFromItem(item); return (item.description = SearchUtils.GetHierarchyPath(go)); } static Texture2D FetchThumbnail(SearchItem item, SearchContext context) { var obj = ObjectFromItem(item); if (obj == null) return null; return item.thumbnail = GetThumbnailForGameObject(obj); } static Texture2D FetchPreview(SearchItem item, SearchContext context, Vector2 size, FetchPreviewOptions options) { var obj = ObjectFromItem(item); if (obj == null) return item.thumbnail; var assetPath = SearchUtils.GetHierarchyAssetPath(obj, true); if (string.IsNullOrEmpty(assetPath)) return item.thumbnail; if (options.HasFlag(FetchPreviewOptions.Large)) { if (AssetPreview.GetAssetPreview(obj) is Texture2D tex) return tex; } return GetAssetPreviewFromPath(assetPath, size, options); } static void TrackSelection(SearchItem item, SearchContext context) { var obj = ObjectFromItem(item); if (obj) Selection.activeGameObject = obj; if (SceneView.lastActiveSceneView != null) SceneView.lastActiveSceneView.FrameSelected(); } static float DistanceHandler(GameObject go, Vector3 p) { return (go.transform.position - p).magnitude; } static Vector3 DistanceParamHandler(string param) { if (param == "selection") { var centerPoint = Selection.gameObjects.Select(go => go.transform.position).Aggregate((v1, v2) => v1 + v2); centerPoint /= Selection.gameObjects.Length; return centerPoint; } if (param.StartsWith("[") && param.EndsWith("]")) { param = param.Trim('[', ']'); var vectorTokens = param.Split(','); var vectorValues = vectorTokens.Select(token => float.Parse(token, CultureInfo.InvariantCulture.NumberFormat)).ToList(); while (vectorValues.Count < 3) vectorValues.Add(0f); return new Vector3(vectorValues[0], vectorValues[1], vectorValues[2]); } var obj = s_GameObjects.FirstOrDefault(go => go.name == param); if (!obj) return Vector3.zero; return obj.transform.position; } static GameObject ObjectFromItem(SearchItem item) { var instanceID = Convert.ToInt32(item.id); var obj = EditorUtility.InstanceIDToObject(instanceID) as GameObject; return obj; } static Texture2D GetThumbnailForGameObject(GameObject go) { var thumbnail = PrefabUtility.GetIconForGameObject(go); if (thumbnail) return thumbnail; return EditorGUIUtility.ObjectContent(go, go.GetType()).image as Texture2D; } static Texture2D GetAssetPreviewFromPath(string path, Vector2 previewSize, FetchPreviewOptions previewOptions) { var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path); if (obj == null) return null; var preview = AssetPreview.GetAssetPreview(obj); if (preview == null || previewOptions.HasFlag(FetchPreviewOptions.Large)) { var largePreview = AssetPreview.GetMiniThumbnail(obj); if (preview == null || (largePreview != null && largePreview.width > preview.width)) preview = largePreview; } return preview; } }
entrySeparators | Separators used to split an entry into indexable tokens. |
FetchGameObjects | Utility function to fetch all the game objects in a particular scene. |
FindShiftLeftVariations | Extract all variations on a word. As an example: the word hello would have the following variations: h, he, hel, hell, hello. |
GetAssetPath | Returns the asset path of a search item if any. |
GetHierarchyAssetPath | Get the path of the scene (or prefab) containing a GameObject. |
GetHierarchyPath | Get the hierarchy path of a GameObject including the scene name if includeScene is set to true. |
GetObjectPath | Get the path of a Unity Object. If it is a GameObject or a Component it is the . Else it is the asset name. |
GetTransformPath | Format the pretty name of a Transform component by appending all the parent hierarchy names. |
MatchSearchGroups | Helper function to match a string against the SearchContext. This will try to match the search query against each token of content (similar to the AddComponent menu workflow). |
SelectMultipleItems | Select and ping multiple objects in the Project Browser. |
SplitCamelCase | Tokenize a string each capital letter. |
SplitEntryComponents | Split an entry according to a specified list of separators. |
SplitFileEntryComponents | Split a file entry according to a list of separators and find all the variations on the entry name. |