when coming back from unreal to unity, you realize that your work goes to a crawl and see that there is no search in the inspector
vital because in production, a gamobject easily contains 10 components totaling 50 variables
when making a decision of changing a variable during tuning, one has to switch context from decision to MANUAL search mode, this context switching kicks you out of flow, which is extremely costly
because unreal has search inspector, it doesn’t require context switch to manual search so it is far better at keeping you in the state of flow, which is simply astonishing because unity is smoother
as a result, instead of much preferring working in unity, which should be the case when you look at the list of pros (for example editor state = game state), preference goes towards unreal.
Hi. Interesting suggestion. We have had limited success doing these type of search fields in parts for example we have something in the property window. It gets complicated when we have to deal with IMGUI and custom property drawers. I’ll bring it up with the team, it would certainly be useful.
Yeah, I don’t like to rant too much, but it is quite baffling that the company making game engine (which is really complex and demanding task) fails to perform in such not-groundbreaking features.
“We have had limited success doing these type of search fields” that’s one way to put it
Yeah, I’m confused by this too, I’ve seen them make similar comments on other things too. Like the title bar that they wanted to change the behavior of and seemingly not knowing basic features of Win32. It seems like obvious things that I’m curious if it’s just management making dumb choices which is admittedly normal for this company.
it’s not like you’re releasing an API so there is no need to over engineer things. no need for indexing either, if users complain about perfs on large arrays then yeah you can add that
so, just release what you can but do it next month, then work your way up to handle customization
I’ve had so many cases where I wanted to build an editor tool and didn’t know how to start but GPT-4 was able to generate an example with just a few prompts. The first example code took two prompts and the second example came from the third prompt after I had already started posting and was curious.
That’s how we get accused of releasing unfinished features. We already have our roadmap and plans, we don’t have space to add things on a whim. They have to be planned for.
There’s a lot more to it than chat gpt would have you believe.
Not to push Unity Devs even further from communicating with us with rants/complaints…but it is abundantly clear there’s way too much bureaucracy involved in the workflow.
Yet still…major issues go released unnoticed, it takes eons for small adjustments and important fixes, QoL features constantly either work poorly or are tossed under the rug. Priorities are always leaving massive question marks for the majority of us.
Constantly having to work around editor/service issues, given the usual shpeal of how complicated it is or that it’s in the works only to see the thread updated by a random pass-byer years later, “Any updates??” followed by apologies and excuses.
On a personal level I’m finding all joy of game development sucked out by working within the Unity editor, and my only hope is improvements on these future versions. Something as simple as a parameter filter as suggested above would be a tiny but solid improvement, but it’ll have to be yet another custom tool working around the editor.
Yes and no. What I generated with ChatGPT is a very minimal proof of concept, but what the team is developing is far more than needed because it has to handle all of the common use cases. If I had proper access to the editor’s interface source code I could make the editor behave in the exact fashion that I wanted it to.
I’m tempted to try modifying the UnityEngine assembly because the source code (albeit in a reverse engineered format rather than the original) is right there. I wouldn’t even need to make extensive changes. In some cases it’s literally just switching access permissions from internal or private to public to enable a feature in a custom editor.
great script, made some tweaks to it, mainly due to how it is presented(didnt care for the text only representation of the original inspector) - search for unity's inspector · GitHub
honestly its so simple, kinda wondering what pitfalls there are for it. One thing I did notice, it wont find fields inside of a serialized struct within a monobehaviour
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Junk.Utilities
{
public static class SearchInspector
{
private static string searchString = "Search...";
private static Dictionary<string, List<SerializedProperty>> matchingProperties = new Dictionary<string, List<SerializedProperty>>();
private static Dictionary<string, SerializedObject> serializedObjectsCache = new Dictionary<string, SerializedObject>();
[InitializeOnLoadMethod]
static void Init()
{
UnityEditor.Editor.finishedDefaultHeaderGUI += OnDisplaySearch;
}
static void OnDisplaySearch(UnityEditor.Editor editor)
{
if (!(editor.target is GameObject))
return;
EditorGUI.BeginChangeCheck();
searchString = EditorGUILayout.TextField(searchString);
if (EditorGUI.EndChangeCheck())
{
if (!string.IsNullOrWhiteSpace(searchString))
{
SearchFieldsAndProperties(editor.target);
}
else
{
matchingProperties.Clear();
serializedObjectsCache.Clear();
}
}
if (matchingProperties.Any())
{
GUILayout.Label("Results:", EditorStyles.boldLabel);
foreach (var kvp in matchingProperties)
{
// Get display name for the parent path.
string parentDisplayName = GetDisplayNameFromPath(kvp.Key);
SerializedObject serializedObject = serializedObjectsCache[kvp.Key];
serializedObject.Update();
EditorGUI.indentLevel++;
// Track if we have shown the struct/class label.
bool shownParentLabel = false;
foreach (SerializedProperty property in kvp.Value)
{
// Check if we should skip the parent label.
if (!shownParentLabel && parentDisplayName != property.displayName)
{
GUILayout.Label(parentDisplayName, EditorStyles.boldLabel);
shownParentLabel = true;
}
EditorGUILayout.PropertyField(property, true);
}
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
}
}
}
private static void SearchFieldsAndProperties(Object searchTarget)
{
matchingProperties.Clear();
serializedObjectsCache.Clear();
if (searchTarget == null) return;
Component[] components = ((GameObject)searchTarget).GetComponents<Component>();
foreach (Component component in components)
{
SerializedObject serializedObject = new SerializedObject(component);
SerializedProperty serializedProperty = serializedObject.GetIterator();
bool enterChildren = true;
while (serializedProperty.NextVisible(enterChildren))
{
if (PropertyMatchesSearch(serializedProperty))
{
AddPropertyIfNotAdded(serializedProperty, serializedObject);
}
else if (serializedProperty.hasChildren)
{
SearchWithinSerializedProperty(serializedProperty, serializedObject);
}
enterChildren = false;
}
}
}
private static bool PropertyMatchesSearch(SerializedProperty property)
{
return property.displayName.ToLowerInvariant().Contains(searchString.ToLowerInvariant());
}
private static void AddPropertyIfNotAdded(SerializedProperty property, SerializedObject serializedObject)
{
string parentPath = GetPropertyParentPath(property);
if (!matchingProperties.ContainsKey(parentPath))
{
matchingProperties[parentPath] = new List<SerializedProperty>();
}
if (!matchingProperties[parentPath].Any(p => p.propertyPath == property.propertyPath))
{
matchingProperties[parentPath].Add(property.Copy());
if (!serializedObjectsCache.ContainsKey(parentPath))
{
serializedObjectsCache[parentPath] = serializedObject;
}
}
}
private static void SearchWithinSerializedProperty(SerializedProperty parentProperty, SerializedObject parentSerializedObject)
{
var childSerializedProperty = parentProperty.Copy();
bool enterChildren = true;
while (childSerializedProperty.NextVisible(enterChildren))
{
if (PropertyMatchesSearch(childSerializedProperty))
{
AddPropertyIfNotAdded(childSerializedProperty, parentSerializedObject);
}
enterChildren = false;
}
}
private static string GetPropertyParentPath(SerializedProperty property)
{
var path = property.propertyPath;
int dotIndex = path.LastIndexOf('.');
if (dotIndex != -1)
{
return path.Substring(0, dotIndex);
}
return property.propertyPath; // Return the full path if no parent is identified
}
private static string GetDisplayNameFromPath(string path)
{
// Convert a path into a user-friendly display name.
var parts = path.Split('.');
if (parts.Length > 0)
{
// Attempt to infer a user-friendly name from the last segment of the path
return ObjectNames.NicifyVariableName(parts.Last());
}
return path; // Fallback to the path if unable to process
}
}
}
Sample class
using System;
using UnityEngine;
public class Foo : MonoBehaviour
{
[Serializable]
public struct MyStruct
{
public int x;
public float someFloat;
public Color color;
}
public MyStruct myStruct;
}