有没有办法在 Unity Inspector 中隐藏 EditorGUILayout.ObjectField 的 "Object picker"?
Is there any way to hide the "Object picker" of an EditorGUILayout.ObjectField in Unity Isnpector?
我只是问是否有可能在自定义检查器中隐藏 "Object Picker"(ObjectField 旁边的小 knob/menu)。我有一些情况下更改被禁用 (DisableGroup),我也想隐藏旋钮,而内容无论如何都无法更改。
同时为了让用户更轻松,我考虑将字段调高 (EditorGUIUtility.SingleLineHeight * 2
) -> 选择器也被拉长了,看起来很糟糕 ^^
示例:
using UnityEditor;
using UnityEngine;
public class Bla : MonoBehaviour {
[CustomEditor(typeof(Bla))]
public class BlaEditor : Editor
{
private AudioClip _clip;
public override void OnInspectorGUI()
{
EditorGUI.BeginDisabledGroup(true);
// do some magic to hide the object picker
_clip = (AudioClip) EditorGUILayout.ObjectField("some label", _clip, typeof(AudioClip), false);
EditorGUI.EndDisabledGroup();
}
}
}
我想坚持使用 ObjectField
而不是简单的标签,原因有二:
- 即使在禁用的 `ObjectField| 上"ping" 功能仍然有效。 (如果单击它,相应的资产会在层次结构中突出显示。)标签显然不是这种情况。
- 用户不应该对外观完全不同的控件感到困惑,但我只想删除一些不必要的混乱。
您可能会找到使用样式表隐藏对象选取器的解决方案。
如果您只想显示一些参考,您可以使用一个基本样式为文本字段的简单按钮,添加图像并自己从代码中 ping 对象。
using UnityEngine;
namespace Test
{
public class TestBehaviour : MonoBehaviour
{
[SerializeField] private bool _audioEnabled;
[SerializeField] private AudioClip _audioClip;
}
}
编辑:
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine;
namespace Test
{
[CustomEditor(typeof(TestBehaviour))]
public class TestBehaviourEditor : Editor
{
private SerializedProperty _clipProp;
private SerializedProperty _audioEnabledProp;
private ObjectField m_ObjectField;
private const BindingFlags FIELD_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private void OnEnable()
{
_clipProp = serializedObject.FindProperty("_audioClip");
_audioEnabledProp = serializedObject.FindProperty("_audioEnabled");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_audioEnabledProp);
if(_audioEnabledProp.boolValue)
EditorGUILayout.PropertyField(_clipProp);
else
{
//TODO: calculate proper layout
var type = target.GetType().GetField(_clipProp.propertyPath, FIELD_BINDING_FLAGS).FieldType;
var clip = _clipProp.objectReferenceValue;
var guiContent = EditorGUIUtility.ObjectContent(clip, type);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Fake ObjectField Button");
var style = new GUIStyle("TextField");
style.fixedHeight = 16;
style.imagePosition = clip ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
if (GUILayout.Button(guiContent, style ) && clip)
EditorGUIUtility.PingObject(clip);
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
}
我有另一个解决方案:忽略这个对象选择器的选择结果,虽然选择器还在,可以显示选择器window,选择up 不行。
(我还是不知道怎么隐藏这个按钮和选择器window,><),这个答案也发布在unity answers
代码如下:
// Register another callback of this object field
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
// In this callback, is a trick
private void DefaultAssetFieldCallback(ChangeEvent<UnityEngine.Object> evt) {
// unregister the callback first
myObjectField.UnregisterValueChangedCallback(DefaultAssetFieldCallback);
// trick: set back to the old value
m_ConfigAssetField.value = evt.previousValue;
// register the callback again
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
}
我需要做一些类似的事情,并找到了一种通过单步执行 UIToolkit 调试器中的 ObjectField 来完成此操作的方法。小对象选择器按钮的类型是隐藏的,所以我们不能真正使用 class 本身。
此解决方案使用的是 UIToolkit,因此很遗憾,它不能与 Unity Editor IMGUI 一起使用,但希望它会对某人有所帮助。
简单步骤的解决方案:
- 找出 ObjectFieldSelector 的 uss 样式 class 是什么。
- 递归搜索 ObjectField 的子项以查找包含 uss 样式的 VisualElement class。
- 将可见性设置为假。
全部完成!
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer: PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var field = new ObjectField(property.displayName);
field.objectType = typeof(MyClass);
var button = FindChild(field, "unity-object-field__selector");
button.visible = false;
return field;
}
private VisualElement FindChild(VisualElement parent, string ussClass)
{
foreach(var child in parent.Children())
{
if (child.ClassListContains(ussClass))
return child;
var subChild = FindChild(child, ussClass);
if (subChild != null)
return subChild;
}
return null;
}
}
我只是问是否有可能在自定义检查器中隐藏 "Object Picker"(ObjectField 旁边的小 knob/menu)。我有一些情况下更改被禁用 (DisableGroup),我也想隐藏旋钮,而内容无论如何都无法更改。
同时为了让用户更轻松,我考虑将字段调高 (EditorGUIUtility.SingleLineHeight * 2
) -> 选择器也被拉长了,看起来很糟糕 ^^
示例:
using UnityEditor;
using UnityEngine;
public class Bla : MonoBehaviour {
[CustomEditor(typeof(Bla))]
public class BlaEditor : Editor
{
private AudioClip _clip;
public override void OnInspectorGUI()
{
EditorGUI.BeginDisabledGroup(true);
// do some magic to hide the object picker
_clip = (AudioClip) EditorGUILayout.ObjectField("some label", _clip, typeof(AudioClip), false);
EditorGUI.EndDisabledGroup();
}
}
}
我想坚持使用 ObjectField
而不是简单的标签,原因有二:
- 即使在禁用的 `ObjectField| 上"ping" 功能仍然有效。 (如果单击它,相应的资产会在层次结构中突出显示。)标签显然不是这种情况。
- 用户不应该对外观完全不同的控件感到困惑,但我只想删除一些不必要的混乱。
您可能会找到使用样式表隐藏对象选取器的解决方案。
如果您只想显示一些参考,您可以使用一个基本样式为文本字段的简单按钮,添加图像并自己从代码中 ping 对象。
using UnityEngine;
namespace Test
{
public class TestBehaviour : MonoBehaviour
{
[SerializeField] private bool _audioEnabled;
[SerializeField] private AudioClip _audioClip;
}
}
编辑:
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine;
namespace Test
{
[CustomEditor(typeof(TestBehaviour))]
public class TestBehaviourEditor : Editor
{
private SerializedProperty _clipProp;
private SerializedProperty _audioEnabledProp;
private ObjectField m_ObjectField;
private const BindingFlags FIELD_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private void OnEnable()
{
_clipProp = serializedObject.FindProperty("_audioClip");
_audioEnabledProp = serializedObject.FindProperty("_audioEnabled");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_audioEnabledProp);
if(_audioEnabledProp.boolValue)
EditorGUILayout.PropertyField(_clipProp);
else
{
//TODO: calculate proper layout
var type = target.GetType().GetField(_clipProp.propertyPath, FIELD_BINDING_FLAGS).FieldType;
var clip = _clipProp.objectReferenceValue;
var guiContent = EditorGUIUtility.ObjectContent(clip, type);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Fake ObjectField Button");
var style = new GUIStyle("TextField");
style.fixedHeight = 16;
style.imagePosition = clip ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
if (GUILayout.Button(guiContent, style ) && clip)
EditorGUIUtility.PingObject(clip);
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
}
我有另一个解决方案:忽略这个对象选择器的选择结果,虽然选择器还在,可以显示选择器window,选择up 不行。
(我还是不知道怎么隐藏这个按钮和选择器window,><),这个答案也发布在unity answers
代码如下:
// Register another callback of this object field
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
// In this callback, is a trick
private void DefaultAssetFieldCallback(ChangeEvent<UnityEngine.Object> evt) {
// unregister the callback first
myObjectField.UnregisterValueChangedCallback(DefaultAssetFieldCallback);
// trick: set back to the old value
m_ConfigAssetField.value = evt.previousValue;
// register the callback again
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
}
我需要做一些类似的事情,并找到了一种通过单步执行 UIToolkit 调试器中的 ObjectField 来完成此操作的方法。小对象选择器按钮的类型是隐藏的,所以我们不能真正使用 class 本身。
此解决方案使用的是 UIToolkit,因此很遗憾,它不能与 Unity Editor IMGUI 一起使用,但希望它会对某人有所帮助。
简单步骤的解决方案:
- 找出 ObjectFieldSelector 的 uss 样式 class 是什么。
- 递归搜索 ObjectField 的子项以查找包含 uss 样式的 VisualElement class。
- 将可见性设置为假。 全部完成!
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer: PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var field = new ObjectField(property.displayName);
field.objectType = typeof(MyClass);
var button = FindChild(field, "unity-object-field__selector");
button.visible = false;
return field;
}
private VisualElement FindChild(VisualElement parent, string ussClass)
{
foreach(var child in parent.Children())
{
if (child.ClassListContains(ussClass))
return child;
var subChild = FindChild(child, ussClass);
if (subChild != null)
return subChild;
}
return null;
}
}