Is there a way to set the m_Name field in MonoBehaviour blocks in YAML files? It would be easier to parse diffs if MmonoBehaviours had human-readable names. Maybe it’s something extra we could be setting programmatically?
GameObject has its m_Name set via the GameObject.name property but I can’t find any way to set it for MonoBehaviours.
It does in the YAML though, it’s just that the field is empty.
It would be easier to figure out which component was added to a GameObject just by looking at the diff (while reviewing a PR, for example) if the component had the m_Name filled out.
Right now we have to look at serialized fields and divine which component was edited (if it has exposed fields at all).
It’s empty because Component.name just points to GameObject.name. The field is otherwise unused by components, even if it has an empty serialised field for it.
I imagine it is probably possible by making a SerializedObject out of a component, and getting the SerializedProperty for m_Name and writing to that.
Hey @spiney199 , I just tested that approach and it works. Now if I could expand this to affect ALL components, not just ones I’ve written and hook up elegantly (ie. not require a per-class call from something like OnValidate to a function that sets m_Name or doing a MonoBehaviour subclass).
Maybe some MonoBehaviour or Editor event I could hook into?
Though it’s not the most specific, so the following code will likely run a lot more often than you would like it to:
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public static class ComponentNameEnforcer
{
static ComponentNameEnforcer()
{
ObjectChangeEvents.changesPublished += HandleChangesPublished;
}
private readonly static List<Component> _componentsCache = new();
private static void HandleChangesPublished(ref ObjectChangeEventStream stream)
{
int length = stream.length;
for (int i = 0; i < length; i++)
{
var eventType = stream.GetEventType(i);
if (eventType == ObjectChangeKind.ChangeGameObjectStructure)
{
stream.GetChangeGameObjectStructureEvent(i, out var data);
EnforceComponentNames(data.instanceId);
}
if (eventType == ObjectChangeKind.ChangeGameObjectOrComponentProperties)
{
stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out var data);
EnforceComponentNames(data.instanceId);
}
}
}
private static void EnforceComponentNames(int instanceID)
{
var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
if (gameObject == null)
{
return;
}
gameObject.GetComponents<Component>(_componentsCache);
foreach (var component in _componentsCache)
{
var serialisedObject = new SerializedObject(component);
var nameProperty = serialisedObject.FindProperty("m_Name");
if (nameProperty != null)
{
nameProperty.stringValue = gameObject.name;
}
serialisedObject.ApplyModifiedProperties();
serialisedObject.Dispose();
}
_componentsCache.Clear();
}
}
#endif
Particularly ObjectChangeKind.ChangeGameObjectOrComponentProperties, which changes whenever any property on a game object changes, including anything in a component inspector. Though that’s the only way to capture when a game object’s name changes.
Yup, that seems to be working albeit for custom components only. Unity components (like Rigidbody2D or Animator) seem to not raise this event.
I did add a slight modification, since the instanceID reported in ChangeGameObjectStructureEventArgs can refer to a Component, not just a GameObject and I wanted the component type to be written into m_Name:
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public static class ComponentNameEnforcer
{
static ComponentNameEnforcer()
{
ObjectChangeEvents.changesPublished += HandleChangesPublished;
}
private static readonly List<Component> ComponentsCache = new();
private static void HandleChangesPublished(ref ObjectChangeEventStream stream)
{
var length = stream.length;
for (var i = 0; i < length; i++)
{
var eventType = stream.GetEventType(i);
if (eventType == ObjectChangeKind.ChangeGameObjectStructure)
{
stream.GetChangeGameObjectStructureEvent(i, out var data);
EnforceComponentNames(data.instanceId);
}
if (eventType == ObjectChangeKind.ChangeGameObjectOrComponentProperties)
{
stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out var data);
EnforceComponentNames(data.instanceId);
}
}
}
private static void EnforceComponentNames(int instanceID)
{
var goOrObject = EditorUtility.InstanceIDToObject(instanceID);
if (goOrObject is GameObject go)
{
go.GetComponents(ComponentsCache);
}
else if (goOrObject is Component comp)
{
ComponentsCache.Add(comp);
}
else
{
return;
}
foreach (var component in ComponentsCache)
{
var serialisedObject = new SerializedObject(component);
var nameProperty = serialisedObject.FindProperty("m_Name");
if (nameProperty != null)
{
nameProperty.stringValue = component.GetType().Name;
}
serialisedObject.ApplyModifiedProperties();
serialisedObject.Dispose();
}
ComponentsCache.Clear();
}
}
#endif
It’s partially what I needed. Thank you @spiney199 for helping out!
I have often felt this pain when using strings for event passing saved in components. Here’s my method for adding info to a diff or enabling searching scene files in VS Code (in the absence of other human readable information):
using UnityEditor.Search;
using UnityEngine;
public class UI_Window : MonoBehaviour
{
[SerializeField] string scriptPath;
private void OnValidate()
{
scriptPath = SearchUtils.GetHierarchyPath(gameObject, false) + "/" + this.GetType().Name;
}
}
The result lets me search for a serialized event name in the component (e.g. “HideCountdown”), and the path tells me know how to find it in the hierarchy.