From 48de7352cfc6db616b2a0e66a267c763dede2f7d Mon Sep 17 00:00:00 2001 From: mika Date: Thu, 17 Apr 2025 14:31:36 +0300 Subject: [PATCH 01/11] Create PasteScript.cs --- Assets/Scripts/Editor/Tools/PasteScript.cs | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/PasteScript.cs diff --git a/Assets/Scripts/Editor/Tools/PasteScript.cs b/Assets/Scripts/Editor/Tools/PasteScript.cs new file mode 100644 index 0000000..84a1561 --- /dev/null +++ b/Assets/Scripts/Editor/Tools/PasteScript.cs @@ -0,0 +1,85 @@ +// new version of the https://github.com/UnityCommunity/UnityLibrary/blob/master/Assets/Scripts/Editor/Tools/CopyPasteHelper.cs +// creates c# script from clipboard when click button + +using UnityEngine; +using UnityEditor; +using System.IO; +using System.Text.RegularExpressions; + +namespace UnityLibrary.Tools +{ + public class PasteScript : EditorWindow + { + private string statusMessage = ""; + private string lastCreatedScriptPath = ""; + + [MenuItem("Tools/UnityLibrary/Clipboard To C# Script")] + public static void ShowWindow() + { + GetWindow("Clipboard to Script"); + } + + void OnGUI() + { + if (GUILayout.Button("Create Script from Clipboard")) + { + TryCreateScriptFromClipboard(); + } + + GUILayout.Space(10); + + // Draw clickable HelpBox + EditorGUILayout.HelpBox(statusMessage, MessageType.Info); + + Rect helpBoxRect = GUILayoutUtility.GetLastRect(); + if (!string.IsNullOrEmpty(lastCreatedScriptPath) && Event.current.type == EventType.MouseDown && helpBoxRect.Contains(Event.current.mousePosition)) + { + var asset = AssetDatabase.LoadAssetAtPath(lastCreatedScriptPath); + if (asset != null) + { + EditorGUIUtility.PingObject(asset); + } + Event.current.Use(); // Consume the click + } + } + + void TryCreateScriptFromClipboard() + { + string clipboard = EditorGUIUtility.systemCopyBuffer; + + if (IsProbablyCSharp(clipboard)) + { + string folderPath = "Assets/Scripts/Generated"; + Directory.CreateDirectory(folderPath); + + string className = GetClassName(clipboard) ?? "GeneratedScript"; + string path = AssetDatabase.GenerateUniqueAssetPath($"{folderPath}/{className}.cs"); + + File.WriteAllText(path, clipboard); + AssetDatabase.Refresh(); + + statusMessage = $"Script created: {path}"; + lastCreatedScriptPath = path; + } + else + { + statusMessage = "Clipboard does not contain valid C# code."; + lastCreatedScriptPath = ""; + } + } + + bool IsProbablyCSharp(string text) + { + if (string.IsNullOrWhiteSpace(text)) return false; + + // Basic heuristic checks + return Regex.IsMatch(text, @"\b(class|struct|interface|using|namespace)\b"); + } + + string GetClassName(string text) + { + Match match = Regex.Match(text, @"\bclass\s+(\w+)"); + return match.Success ? match.Groups[1].Value : null; + } + } +} From 8b590f2c74ee4af3424e08c1cc8dc466021a9711 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 25 Apr 2025 11:43:12 +0300 Subject: [PATCH 02/11] Create GameViewGridOverlay.cs --- .../Editor/Tools/GameViewGridOverlay.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs diff --git a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs new file mode 100644 index 0000000..a7f87ae --- /dev/null +++ b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs @@ -0,0 +1,45 @@ +// draws grid lines in the game view (useful for seeing the resolution of ui elements in the game view) +// usage: attach to a game object in the scene, set gameobject tag to "EditorOnly" to remove from builds + +using UnityEngine; + +namespace UnityLibrary.EditorTools +{ + [ExecuteAlways] + public class GameViewGridOverlay : MonoBehaviour + { +#if UNITY_EDITOR + public bool drawGrid = true; + + public int gridSpacingX = 64; + public int gridSpacingY = 64; + + public int startOffsetX = 0; + public int startOffsetY = 0; + + public Color gridColor = new Color(1f, 1f, 1f, 0.5f); + + private void OnGUI() + { + if (!drawGrid || Application.isPlaying) return; + + Color oldColor = GUI.color; + GUI.color = gridColor; + + // Horizontal lines + for (int y = startOffsetX; y < Screen.height; y += gridSpacingY) + { + GUI.DrawTexture(new Rect(0, y, Screen.width, 1), Texture2D.whiteTexture); + } + + // Vertical lines + for (int x = startOffsetY; x < Screen.width; x += gridSpacingX) + { + GUI.DrawTexture(new Rect(x, 0, 1, Screen.height), Texture2D.whiteTexture); + } + + GUI.color = oldColor; + } +#endif + } +} From 437db6375f25c0d47d08b8cf543dc3a7e5c754f0 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 25 Apr 2025 11:48:26 +0300 Subject: [PATCH 03/11] Create README.md --- Assets/Scripts/Editor/Tools/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/README.md diff --git a/Assets/Scripts/Editor/Tools/README.md b/Assets/Scripts/Editor/Tools/README.md new file mode 100644 index 0000000..20d6e2a --- /dev/null +++ b/Assets/Scripts/Editor/Tools/README.md @@ -0,0 +1,2 @@ +### GameViewGridOverlay.cs +![Image](https://github.com/user-attachments/assets/48fbced4-48e0-49fe-9acc-666f5449a958) From acb71e7a1e95430c371cdf3e054986f200a858c5 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 25 Apr 2025 12:31:50 +0300 Subject: [PATCH 04/11] Update GameViewGridOverlay.cs --- Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs index a7f87ae..143bedc 100644 --- a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs +++ b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs @@ -26,14 +26,14 @@ private void OnGUI() Color oldColor = GUI.color; GUI.color = gridColor; - // Horizontal lines - for (int y = startOffsetX; y < Screen.height; y += gridSpacingY) + // vertical lines + for (int y = startOffsetY; y < Screen.height; y += gridSpacingY) { GUI.DrawTexture(new Rect(0, y, Screen.width, 1), Texture2D.whiteTexture); } - // Vertical lines - for (int x = startOffsetY; x < Screen.width; x += gridSpacingX) + // horizontal lines + for (int x = startOffsetX; x < Screen.width; x += gridSpacingX) { GUI.DrawTexture(new Rect(x, 0, 1, Screen.height), Texture2D.whiteTexture); } From 69fe35d66df49842a2152e6bb6f7c472cdc2f939 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 25 Apr 2025 12:51:50 +0300 Subject: [PATCH 05/11] Update GameViewGridOverlay.cs - adding spacing support --- .../Editor/Tools/GameViewGridOverlay.cs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs index 143bedc..380bf2a 100644 --- a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs +++ b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs @@ -1,6 +1,3 @@ -// draws grid lines in the game view (useful for seeing the resolution of ui elements in the game view) -// usage: attach to a game object in the scene, set gameobject tag to "EditorOnly" to remove from builds - using UnityEngine; namespace UnityLibrary.EditorTools @@ -11,9 +8,15 @@ public class GameViewGridOverlay : MonoBehaviour #if UNITY_EDITOR public bool drawGrid = true; - public int gridSpacingX = 64; - public int gridSpacingY = 64; + [Header("Grid Cell Size (visible area)")] + public int gridSizeX = 64; + public int gridSizeY = 64; + + [Header("Spacing Between Cells (invisible gap)")] + public int spacingX = 16; + public int spacingY = 16; + [Header("Start Offsets")] public int startOffsetX = 0; public int startOffsetY = 0; @@ -26,16 +29,22 @@ private void OnGUI() Color oldColor = GUI.color; GUI.color = gridColor; - // vertical lines - for (int y = startOffsetY; y < Screen.height; y += gridSpacingY) - { - GUI.DrawTexture(new Rect(0, y, Screen.width, 1), Texture2D.whiteTexture); - } + int cellStrideX = gridSizeX + spacingX; + int cellStrideY = gridSizeY + spacingY; - // horizontal lines - for (int x = startOffsetX; x < Screen.width; x += gridSpacingX) + for (int y = startOffsetY; y + gridSizeY <= Screen.height; y += cellStrideY) { - GUI.DrawTexture(new Rect(x, 0, 1, Screen.height), Texture2D.whiteTexture); + for (int x = startOffsetX; x + gridSizeX <= Screen.width; x += cellStrideX) + { + // Left line + GUI.DrawTexture(new Rect(x, y, 1, gridSizeY), Texture2D.whiteTexture); + // Right line + GUI.DrawTexture(new Rect(x + gridSizeX - 1, y, 1, gridSizeY), Texture2D.whiteTexture); + // Top line + GUI.DrawTexture(new Rect(x, y, gridSizeX, 1), Texture2D.whiteTexture); + // Bottom line + GUI.DrawTexture(new Rect(x, y + gridSizeY - 1, gridSizeX, 1), Texture2D.whiteTexture); + } } GUI.color = oldColor; From 61f149cbada8d0668cf65d95ede339975156a439 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 25 Apr 2025 12:54:11 +0300 Subject: [PATCH 06/11] Update GameViewGridOverlay.cs fix: need to draw past screensize to see last edges --- .../Scripts/Editor/Tools/GameViewGridOverlay.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs index 380bf2a..06acb2e 100644 --- a/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs +++ b/Assets/Scripts/Editor/Tools/GameViewGridOverlay.cs @@ -32,17 +32,20 @@ private void OnGUI() int cellStrideX = gridSizeX + spacingX; int cellStrideY = gridSizeY + spacingY; - for (int y = startOffsetY; y + gridSizeY <= Screen.height; y += cellStrideY) + // Loop until start of the cell is beyond screen, not end of cell + for (int y = startOffsetY; y < Screen.height; y += cellStrideY) { - for (int x = startOffsetX; x + gridSizeX <= Screen.width; x += cellStrideX) + for (int x = startOffsetX; x < Screen.width; x += cellStrideX) { - // Left line + // Draw full box even if it goes beyond screen edges + + // Left GUI.DrawTexture(new Rect(x, y, 1, gridSizeY), Texture2D.whiteTexture); - // Right line + // Right GUI.DrawTexture(new Rect(x + gridSizeX - 1, y, 1, gridSizeY), Texture2D.whiteTexture); - // Top line + // Top GUI.DrawTexture(new Rect(x, y, gridSizeX, 1), Texture2D.whiteTexture); - // Bottom line + // Bottom GUI.DrawTexture(new Rect(x, y + gridSizeY - 1, gridSizeX, 1), Texture2D.whiteTexture); } } From 5ede7f09e9f63080223111ed2e09b4473e33dd92 Mon Sep 17 00:00:00 2001 From: mika Date: Mon, 28 Apr 2025 14:59:35 +0300 Subject: [PATCH 07/11] Create RectTransformCloner.cs --- .../Editor/Tools/RectTransformCloner.cs | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/RectTransformCloner.cs diff --git a/Assets/Scripts/Editor/Tools/RectTransformCloner.cs b/Assets/Scripts/Editor/Tools/RectTransformCloner.cs new file mode 100644 index 0000000..fd55097 --- /dev/null +++ b/Assets/Scripts/Editor/Tools/RectTransformCloner.cs @@ -0,0 +1,151 @@ +// clones UI RectTransform values from one GameObject to another in Unity Editor (only for identical hierarchy) + +using UnityEngine; +using UnityEditor; +using TMPro; +using UnityEngine.UI; + +namespace UnityLibrary.Tools +{ + public class RectTransformCloner : EditorWindow + { + private GameObject source; + private GameObject target; + private bool requireIdenticalNames = true; + private bool cloneTMPAlignment = false; + + [MenuItem("Tools/RectTransform Cloner")] + private static void ShowWindow() + { + var window = GetWindow(); + window.titleContent = new GUIContent("RectTransform Cloner"); + window.Show(); + } + + private void OnGUI() + { + GUILayout.Label("Clone RectTransform", EditorStyles.boldLabel); + + source = (GameObject)EditorGUILayout.ObjectField("Source", source, typeof(GameObject), true); + target = (GameObject)EditorGUILayout.ObjectField("Target", target, typeof(GameObject), true); + requireIdenticalNames = EditorGUILayout.Toggle("Require Identical Names", requireIdenticalNames); + cloneTMPAlignment = EditorGUILayout.Toggle("Clone TMP Alignment", cloneTMPAlignment); + + if (GUILayout.Button("Clone RectTransforms")) + { + if (source == null || target == null) + { + Debug.LogError("Source and Target must be assigned."); + return; + } + + string errorMessage; + if (!CompareHierarchies(source.transform, target.transform, out errorMessage)) + { + Debug.LogError("Source and Target hierarchies do not match!\n" + errorMessage, target); + return; + } + + Undo.RegisterFullObjectHierarchyUndo(target, "Clone RectTransform Values"); + CopyRectTransforms(source.transform, target.transform); + + Debug.Log("RectTransform values cloned successfully.", target); + + if (target.transform.parent != null) + { + RectTransform parentRect = target.transform.parent as RectTransform; + if (parentRect != null) + { + LayoutRebuilder.ForceRebuildLayoutImmediate(parentRect); + } + else + { + Debug.LogWarning("Target's parent is not a RectTransform, cannot force layout rebuild.", target); + } + } + else + { + Debug.LogWarning("Target has no parent, cannot force layout rebuild.", target); + } + EditorUtility.SetDirty(target); + SceneView.RepaintAll(); + } + } + + private bool CompareHierarchies(Transform source, Transform target, out string errorMessage) + { + errorMessage = ""; + + if (source.childCount != target.childCount) + { + errorMessage = $"Child count mismatch at {GetTransformPath(source)}: Source has {source.childCount}, Target has {target.childCount}"; + return false; + } + + for (int i = 0; i < source.childCount; i++) + { + var sourceChild = source.GetChild(i); + var targetChild = target.GetChild(i); + + if (requireIdenticalNames && sourceChild.name != targetChild.name) + { + errorMessage = $"Child name mismatch at {GetTransformPath(sourceChild)}: Source has '{sourceChild.name}', Target has '{targetChild.name}'"; + return false; + } + + if (!CompareHierarchies(sourceChild, targetChild, out errorMessage)) + { + return false; + } + } + + return true; + } + + private void CopyRectTransforms(Transform source, Transform target) + { + var sourceRect = source as RectTransform; + var targetRect = target as RectTransform; + + if (sourceRect != null && targetRect != null) + { + CopyRectTransformValues(sourceRect, targetRect); + + if (cloneTMPAlignment) + { + var sourceTMP = source.GetComponent(); + var targetTMP = target.GetComponent(); + if (sourceTMP != null && targetTMP != null) + { + Undo.RecordObject(targetTMP, "Clone TMP Alignment"); + targetTMP.alignment = sourceTMP.alignment; + } + } + } + + for (int i = 0; i < source.childCount; i++) + { + CopyRectTransforms(source.GetChild(i), target.GetChild(i)); + } + } + + private void CopyRectTransformValues(RectTransform source, RectTransform target) + { + target.anchoredPosition = source.anchoredPosition; + target.sizeDelta = source.sizeDelta; + target.anchorMin = source.anchorMin; + target.anchorMax = source.anchorMax; + target.pivot = source.pivot; + target.localRotation = source.localRotation; + target.localScale = source.localScale; + target.localPosition = source.localPosition; + } + + private string GetTransformPath(Transform t) + { + if (t.parent == null) + return t.name; + return GetTransformPath(t.parent) + "/" + t.name; + } + } +} From 4df3c80b60fc36d07a85ad241f2ca16c9aa277aa Mon Sep 17 00:00:00 2001 From: mika Date: Thu, 15 May 2025 21:34:22 +0300 Subject: [PATCH 08/11] Create FindWhoReferencesThisGameObject.cs --- .../Tools/FindWhoReferencesThisGameObject.cs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs diff --git a/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs b/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs new file mode 100644 index 0000000..b2def9e --- /dev/null +++ b/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs @@ -0,0 +1,123 @@ +// finds what scripts reference a given GameObject in the scene (in events, public fields..) + +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using UnityEngine.Events; + +namespace UnityLibrary.Editor +{ + public class FindWhoReferencesThisGameObject : EditorWindow + { + private GameObject target; + private Vector2 scroll; + + private class ReferenceResult + { + public string message; + public GameObject owner; + } + + private List results = new List(); + + [MenuItem("Tools/UnityLibrary/Find References To GameObject")] + public static void ShowWindow() + { + var win = GetWindow("Find References"); + win.minSize = new Vector2(500, 300); + } + + private void OnGUI() + { + GUILayout.Label("Find scripts that reference this GameObject", EditorStyles.boldLabel); + target = EditorGUILayout.ObjectField("Target GameObject", target, typeof(GameObject), true) as GameObject; + + if (GUILayout.Button("Find References")) + { + results.Clear(); + if (target != null) + { + FindReferences(target); + } + else + { + Debug.LogWarning("Please assign a GameObject."); + } + } + + if (results.Count > 0) + { + GUILayout.Label("Results:", EditorStyles.boldLabel); + scroll = GUILayout.BeginScrollView(scroll, GUILayout.Height(400)); + foreach (var res in results) + { + if (GUILayout.Button(res.message, GUILayout.ExpandWidth(true))) + { + EditorGUIUtility.PingObject(res.owner); + Selection.activeGameObject = res.owner; + } + } + GUILayout.EndScrollView(); + } + } + + private void FindReferences(GameObject target) + { + var allObjects = UnityEngine.Object.FindObjectsOfType(true); + + foreach (var mono in allObjects) + { + if (mono == null || mono.gameObject == target) continue; + + var type = mono.GetType(); + var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (var field in fields) + { + if (typeof(UnityEventBase).IsAssignableFrom(field.FieldType)) + { + var unityEvent = field.GetValue(mono) as UnityEventBase; + if (unityEvent != null) + { + int count = unityEvent.GetPersistentEventCount(); + for (int i = 0; i < count; i++) + { + var listener = unityEvent.GetPersistentTarget(i); + if (listener == target) + { + results.Add(new ReferenceResult + { + message = $"{mono.name} ({type.Name}) -> UnityEvent '{field.Name}'", + owner = mono.gameObject + }); + } + } + } + } + else if (typeof(UnityEngine.Object).IsAssignableFrom(field.FieldType)) + { + var value = field.GetValue(mono) as UnityEngine.Object; + if (value == target) + { + results.Add(new ReferenceResult + { + message = $"{mono.name} ({type.Name}) -> Field '{field.Name}'", + owner = mono.gameObject + }); + } + } + } + } + + if (results.Count == 0) + { + results.Add(new ReferenceResult + { + message = "No references found.", + owner = null + }); + } + } + } +} From 540d629be475e7e984b893013a5634c725c3ad74 Mon Sep 17 00:00:00 2001 From: mika Date: Thu, 15 May 2025 21:35:50 +0300 Subject: [PATCH 09/11] Update FindWhoReferencesThisGameObject.cs --- Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs b/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs index b2def9e..dd5b2c5 100644 --- a/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs +++ b/Assets/Scripts/Editor/Tools/FindWhoReferencesThisGameObject.cs @@ -1,4 +1,4 @@ -// finds what scripts reference a given GameObject in the scene (in events, public fields..) +// find what scripts reference selected GameObject in the scene (in events, public fields..) using System.Collections.Generic; using System.Reflection; From 15f58004d059406de92b0c1e6668ba08c9e8a168 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 5 Dec 2025 14:35:07 +0200 Subject: [PATCH 10/11] Add SceneTextSearchWindow for scene text searching --- .../Editor/Tools/SceneTextSearchWindow.cs | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 Assets/Scripts/Editor/Tools/SceneTextSearchWindow.cs diff --git a/Assets/Scripts/Editor/Tools/SceneTextSearchWindow.cs b/Assets/Scripts/Editor/Tools/SceneTextSearchWindow.cs new file mode 100644 index 0000000..8467f3e --- /dev/null +++ b/Assets/Scripts/Editor/Tools/SceneTextSearchWindow.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace UnityLibrary.EditorTools +{ + public class SceneTextSearchWindow : EditorWindow + { + [Serializable] + private class SearchResult + { + public Component component; + public string text; + } + + private string searchTerm = string.Empty; + private string previousSearchTerm = string.Empty; + private bool caseSensitive = false; + private bool automaticSearch = true; + + private readonly List results = new List(); + private readonly HashSet seenComponents = new HashSet(); + private Vector2 scrollPos; + + [MenuItem("Tools/UnityLibrary/Scene Text Search")] + public static void Open() + { + var window = GetWindow("Scene Text Search"); + window.minSize = new Vector2(600, 300); + } + + private void OnGUI() + { + EditorGUILayout.LabelField("Search text in loaded scenes", EditorStyles.boldLabel); + EditorGUILayout.Space(); + + EditorGUILayout.BeginHorizontal(); + + EditorGUILayout.LabelField("Search term", GUILayout.Width(80)); + + string newSearchTerm = EditorGUILayout.TextField(searchTerm); + + if (GUILayout.Button("Search", GUILayout.Width(80))) + { + DoSearch(); + } + + EditorGUILayout.EndHorizontal(); + + if (newSearchTerm != searchTerm) + { + searchTerm = newSearchTerm; + + if (automaticSearch) + { + if (!string.IsNullOrEmpty(searchTerm) && searchTerm.Length > 1) + { + DoSearch(); + } + else + { + results.Clear(); + seenComponents.Clear(); + } + } + + previousSearchTerm = searchTerm; + } + + EditorGUILayout.BeginHorizontal(); + caseSensitive = EditorGUILayout.Toggle("Case sensitive", caseSensitive); + automaticSearch = EditorGUILayout.Toggle("Automatic search", automaticSearch); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField($"Results: {results.Count}", EditorStyles.boldLabel); + + scrollPos = EditorGUILayout.BeginScrollView(scrollPos); + foreach (var r in results) + { + if (r.component == null) + continue; + + EditorGUILayout.BeginHorizontal(); + + EditorGUILayout.ObjectField(r.component, typeof(Component), true, GUILayout.Width(220)); + + using (new EditorGUI.DisabledScope(true)) + { + EditorGUILayout.TextField(Truncate(r.text, 200)); + } + + EditorGUILayout.EndHorizontal(); + } + EditorGUILayout.EndScrollView(); + } + + private void DoSearch() + { + results.Clear(); + seenComponents.Clear(); + + if (string.IsNullOrEmpty(searchTerm)) + return; + + string term = caseSensitive ? searchTerm : searchTerm.ToLowerInvariant(); + + var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + + // UnityEngine.UI.Text + SearchComponents(t => t.text, term); + + // TMP UGUI + SearchComponents(t => t.text, term); + + // TMP 3D + SearchComponents(t => t.text, term); + + // Legacy TextMesh + SearchComponents(t => t.text, term); + + // Generic "other text components" with a string 'text' property/field + SearchGenericTextComponents(term); + + stopwatch.Stop(); + Debug.Log($"SceneTextSearchWindow: Found {results.Count} results in {stopwatch.ElapsedMilliseconds} ms"); + } + + private void SearchComponents(Func getText, string term) where T : Component + { + var objects = Resources.FindObjectsOfTypeAll(); + foreach (var comp in objects) + { + if (!IsSceneObject(comp)) + continue; + + string value = getText(comp); + if (StringMatches(value, term)) + { + AddResult(comp, value); + } + } + } + + private void SearchGenericTextComponents(string term) + { + var monos = Resources.FindObjectsOfTypeAll(); + foreach (var mb in monos) + { + if (!IsSceneObject(mb)) + continue; + + if (seenComponents.Contains(mb)) + continue; + + Type type = mb.GetType(); + + try + { + var prop = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (prop != null && prop.PropertyType == typeof(string) && prop.CanRead) + { + string value = prop.GetValue(mb, null) as string; + if (StringMatches(value, term)) + { + AddResult(mb, value); + continue; + } + } + } + catch + { + } + + try + { + var field = type.GetField("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (field != null && field.FieldType == typeof(string)) + { + string value = field.GetValue(mb) as string; + if (StringMatches(value, term)) + { + AddResult(mb, value); + } + } + } + catch + { + } + } + } + + private bool StringMatches(string value, string term) + { + if (string.IsNullOrEmpty(value)) + return false; + + if (!caseSensitive) + value = value.ToLowerInvariant(); + + return value.Contains(term); + } + + private void AddResult(Component component, string text) + { + if (component == null) + return; + + if (seenComponents.Add(component)) + { + results.Add(new SearchResult + { + component = component, + text = text + }); + } + } + + private static bool IsSceneObject(Component comp) + { + if (comp == null) + return false; + + var go = comp.gameObject; + if (go == null) + return false; + + if (EditorUtility.IsPersistent(go)) + return false; + + if (!go.scene.IsValid() || !go.scene.isLoaded) + return false; + + return true; + } + + private static string Truncate(string input, int maxLength) + { + if (string.IsNullOrEmpty(input)) + return input; + if (input.Length <= maxLength) + return input; + return input.Substring(0, maxLength) + "..."; + } + } +} From e08b7a9587c735d5befb476adbcb2e102fc2dc88 Mon Sep 17 00:00:00 2001 From: mika Date: Fri, 5 Dec 2025 14:35:52 +0200 Subject: [PATCH 11/11] Add images for GameViewGridOverlay and SceneTextSearchWindow --- Assets/Scripts/Editor/Tools/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Assets/Scripts/Editor/Tools/README.md b/Assets/Scripts/Editor/Tools/README.md index 20d6e2a..fcdb9c8 100644 --- a/Assets/Scripts/Editor/Tools/README.md +++ b/Assets/Scripts/Editor/Tools/README.md @@ -1,2 +1,5 @@ ### GameViewGridOverlay.cs ![Image](https://github.com/user-attachments/assets/48fbced4-48e0-49fe-9acc-666f5449a958) + +### SceneTextSearchWindow.cs +image