- Home /
ObjectField functions ignoring parameter
I’m attempting to use the SerialzedProperty versions of the EditorGUILayout.ObjectField function to display a custom ScriptableObject, but it ignores me when I try to specify the object's type.
Background/details:
The custom ScriptableObject implements inheritance; a BaseClass (derived from ScriptableObject), with two other classes derived from that (DerivedClassA, and DerivedClassB).
In my editor code, the SerializedProperty I'm trying to draw references a member of the sample MonoBehaviour. This member is declared (but not instantiated) as a BaseClass.
Main issue:
In my editor code, I will use some logic to determine if I want to allow the user to select one or the other of the derived classes. (In the sample below, I just display them all, as tests)
Unfortunately, when I use the SerializedProperty version of the ObjectField function, that takes a Type as a parameter, the type parameter is ignored.
Other tests:
However, If I used the Object (rather than the SerializedProperty) versions of the ObjectFiled function, it DOES use the Type parameter properly.
Question:
Am I doing something wrong? Why does the ObjectField function ignore the provided parameter when I use the SerializedProperty version?
Code:
Here is my test project’s code. It is the editor file, at the bottom, that contains the problematic function calls. You can use the menu items to create test objects of each derived class, in the project folder, if you want to try it out. I'm also attaching a "package" (zipped), so no need to copy/paste, if you want to try it out.
BaseClass.cs
using UnityEngine;
public class BaseClass : ScriptableObject {
public int anInt;
}
DerivedClassA.cs
using UnityEditor;
public class DerivedClassA : BaseClass {
public float aFloat;
[MenuItem("GameObject/MyCategory/DerivedClassA Object", false, 10)]
static void CreateCustomDerivedClassA(MenuCommand menuCommand)
{
// Create a custom game object
DerivedClassA instance = DerivedClassA.CreateInstance<DerivedClassA>();
AssetDatabase.CreateAsset(instance, "Assets/MyDerivedClassA.mat");
}
}
DerivedClassB.cs
using UnityEditor;
public class DerivedClassB : BaseClass {
public string aString = "a";
[MenuItem("GameObject/MyCategory/DerivedClassB Object", false, 10)]
static void CreateCustomDerivedClassA(MenuCommand menuCommand)
{
// Create a custom game object
DerivedClassB instance = DerivedClassB.CreateInstance<DerivedClassB>();
AssetDatabase.CreateAsset(instance, "Assets/MyDerivedClassB.mat");
}
}
HoldingMono.cs
using UnityEngine;
public class HoldingMono : MonoBehaviour {
public BaseClass baseClass;
}
HoldingMonoEditor.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(HoldingMono))]
public class HoldingMonoEditor : Editor
{
SerializedProperty baseClassProperty;
void OnEnable()
{
baseClassProperty = serializedObject.FindProperty("baseClass");
}
public override void OnInspectorGUI()
{
EditorGUILayout.ObjectField(baseClassProperty, typeof(DerivedClassA));// allows selection of any BaseClass objects, and shows the type BaseClass when empty
EditorGUILayout.ObjectField(baseClassProperty, typeof(DerivedClassB));// allows selection of any BaseClass objects, and shows the type BaseClass when empty
baseClassProperty.objectReferenceValue=EditorGUILayout.ObjectField(new GUIContent("DerivedClassA"),baseClassProperty.objectReferenceValue, typeof(DerivedClassA),false);// allows selection of only DerivedClassA objects, and shows the type DerivedClassA whgen empty
baseClassProperty.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent("DerivedClassB"), baseClassProperty.objectReferenceValue, typeof(DerivedClassB), false);// allows selection of only DerivedClassB objects, and shows the type DerivedClassB when empty
}
}
Answer by Bunny83 · Oct 23, 2017 at 09:53 PM
Yes, the Type parameter is in fact ignored. Have a look at this:
private static void ObjectFieldInternal(Rect position, SerializedProperty property, Type objType, GUIContent label, GUIStyle style)
{
int controlID = GUIUtility.GetControlID(EditorGUI.s_PPtrHash, FocusType.Keyboard, position);
position = EditorGUI.PrefixLabel(position, controlID, label);
bool allowSceneObjects = false;
if (property != null)
{
Object targetObject = property.serializedObject.targetObject;
if (targetObject != null && !EditorUtility.IsPersistent(targetObject))
{
allowSceneObjects = true;
}
}
EditorGUI.DoObjectField(position, position, controlID, null, null, property, null, allowSceneObjects, style);
}
As you can see the "objType" parameter is not used here. It should have been passed instead of the "null" infront of the property parameter in the last line. The first null is an UnityEngine.Object reference. The method DoObjectsField is actually used by all ObjectField methods. So either "property" is null or that object reference is null.
So you can probably call it a bug. Note that i don't use the latest Unity version but that's how it looks like in "Unity 5.6.1f1". The SerializedProperty version of ObjectField is actually relative new. It already existed as internal methods but those didn't have a Type argument. So it's probably a copy&paste error. File a bugreport and get it fixed in a year ^^
The reason why the "UnityEngine.Object" version works is that it directly uses DoObjectField like this:
public static Object ObjectField(Rect position, Object obj, Type objType, bool allowSceneObjects)
{
int controlID = GUIUtility.GetControlID(EditorGUI.s_ObjectFieldHash, FocusType.$$anonymous$$eyboard, position);
return EditorGUI.DoObjectField(EditorGUI.IndentedRect(position), EditorGUI.IndentedRect(position), controlID, obj, objType, null, null, allowSceneObjects);
}
Thanks, Bunny! Bug submission complete: https://fogbugz.unity3d.com/default.asp?961878_20mllpnjoe3393ne @Adam-$$anonymous$$echtley FYI
As a temporary workaround you could use this reflection solution. Unfortunately it's not possible to create a delegate for the method (to improve performance and avoid garbage) since that "ObjectFieldValidator" is an internal type and you can't create a delegate that would be compatible with the methods parameter list.
Though I cached as much as possible. Since Unity's Rect struct has a Set method we can even avoid boxing the position all the time by simply creating a delegate for a single boxed value that is reused each time. The only thing that can't be avoided is the single boxed "int" for the controlID as there's no way to set a boxed integer value to another value without re-boxing the value again.
$$anonymous$$aybe it got a bit too far with the optimisations, but it seems to work ^^
So that solution basically replaces the bugged version, and invokes the internal DoObjectField itself? Nice!
Your answer
Follow this Question
Related Questions
How to Find Assets of the Same Type in Editor Script 1 Answer
Make a PropertyField() for a Texture2D SerializedProperty looking like an ObjectField() 2 Answers
EditorGUILayout.ObjectField for Array 1 Answer
why I Cant Add Multiple Objects to the "Object Field" in Editor Window? 3 Answers
Display Custom Inspectors for each class in a List<> 1 Answer