Search Unity

4.6.4f: SerializedProperty in PropertyDrawer has null objectReferenceValue

Discussion in 'Immediate Mode GUI (IMGUI)' started by ianmccleary, May 25, 2015.

  1. ianmccleary

    ianmccleary

    Joined:
    Oct 8, 2012
    Posts:
    31
    Hello,

    I'm using a PropertyDrawer to change how a specific type is drawn in the inspector.
    So far that part works very well. It draws the name of the property and a button besides it:

    The class that is being drawn:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace ToastieRepublic.FX
    5. {
    6.     [System.Serializable]
    7.     public class EffectArray : UnityEngine.Object
    8.     {
    9.         // ...
    10.     }
    11. }
    Then on my PlayerController I have:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace ToastieRepublic.Characters.Player
    5. {
    6.     [System.Serializable]
    7.     public class PlayerController : MonoBehaviour
    8.     {
    9.         // ...
    10.         public EffectArray runDust;
    11.         public EffectArray sprintStartDustLeft;
    12.         public EffectArray sprintStartDustRight;
    13.         public EffectArray sprintDust;
    14.         // ...
    15.     }
    16.  
    17. }
    The result:
    CustomPropertyDrawer.png

    However, inside the PropertyDrawer's OnGUI the objectReferenceValue is always null!
    I looked around some, and it seems like objectReferenceValue is always null unless the object you're serializing is a GameObject, or some other Unity-specific type.

    Even stranger, when I try to call "SerializedProperty.serializedObject.targetObject" it returns a reference to the PlayerController that has the "runDust", "sprintStartDustLeft", "sprintStartDustRight" and "sprintDust" objects rather than the EffectArray instance.

    This code:
    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2. {
    3.  
    4.     // ...
    5.  
    6.     if (GUI.Button(buttonRect, "Configure"))
    7.     {
    8.         string result = string.Empty;
    9.         if (property.objectReferenceValue == null)
    10.             result = "NULL";
    11.         else
    12.             result = property.objectReferenceValue.ToString();
    13.    
    14.         Debug.Log("property.objectReferenceValue: " + result);
    15.    
    16.         result = property.serializedObject.targetObject.ToString();
    17.         Debug.Log("property.serializedObject.targetObject: " + result);
    18.     }
    19. }
    Prints out this:
    DebugLog.png

    Is this a bug? All I'm really trying to do is get a reference to the thing that is being drawn with the property drawer. I'd much rather do this than make my own Editor for the PlayerController object.
     
    Last edited: May 25, 2015
  2. ianmccleary

    ianmccleary

    Joined:
    Oct 8, 2012
    Posts:
    31
    For additional reference I tried casting the targetObject to PlayerController. Works as expected:
    Code (CSharp):
    1.  
    2. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    3. {
    4.     // ...
    5.    
    6.     if (GUI.Button(buttonRect, "Configure"))
    7.     {
    8.         string result = string.Empty;
    9.         if (property.objectReferenceValue == null)
    10.             result = "NULL";
    11.         else
    12.             result = property.objectReferenceValue.ToString();
    13.        
    14.         Debug.Log("property.objectReferenceValue: " + result);
    15.        
    16.         result = property.serializedObject.targetObject.ToString();
    17.         Debug.Log("property.serializedObject.targetObject: " + result);
    18.        
    19.         PlayerController pc = property.serializedObject.targetObject as PlayerController;
    20.         if (pc != null)
    21.             Debug.Log("Player controller information: " + pc.speed);
    22.         else
    23.             Debug.Log("Player controller is null");
    24.     }
    25. }
    It correctly prints out the speed value in the player controller.
    PlayerControllerCast.png

    Obviously this wouldn't work if the EffectArray instance wasn't in a PlayerController, but that isn't really the problem here. Why is it giving me the reference to the PlayerController instead of the EffectArray? Isn't that what is being serialized?
     
  3. ianmccleary

    ianmccleary

    Joined:
    Oct 8, 2012
    Posts:
    31
    After a couple of minutes of thinking and irrational anger, I realized it makes sense for SerializedObject to return the component. SerializedProperty is the property, SerializedObject is the object (component). However, I still do not know how to get the reference to the EffectArray from within the PropertyDrawer.
     
  4. ianmccleary

    ianmccleary

    Joined:
    Oct 8, 2012
    Posts:
    31
    So I realized a couple of things.

    I had EffectArray extend UnityEngine.Object (which is different from System.Object).

    This caused Unity to treat instances of EffectArray as references, the same way it would treat a public Transform. This meant that the object was null. If I were to drag-and-drop an instance of EffectArray onto the inspector, that's the reference I would get when calling property.objectReferenceValue.

    When I take off the UnityEngine.Object, it serializes the object and allows me to change the values of the property directly in the inspector. Now, it's not referencing anything else, so objectReferenceValue doesn't make sense in this case.

    So my solution is to use System.Object and just change values through SerializedProperties and SerializedProperty.FindRelative.
     
    vedram likes this.