Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question [SerializeReference] and PropertyDrawers not serializing?

Discussion in 'Scripting' started by NeedsLoomis, Aug 8, 2023.

  1. NeedsLoomis

    NeedsLoomis

    Joined:
    Mar 22, 2017
    Posts:
    55
    Im trying to write a property drawer where one can select a serialized reference, but I can't get the selection to stick. I've whittled it down to the smallest example, and I can't figure out where Im going wrong...

    The object I wish to serialize:
    Code (CSharp):
    1. [Serializable]
    2. public class ReferenceType
    3. {
    4.     public ReferenceType() { this.name = "Nothing"; }
    5.     public ReferenceType(string name) { this.name = name; }
    6.  
    7.     public readonly string name;
    8. }
    The Mono that is referencing it:
    Code (CSharp):
    1. public class ReferenceTester : MonoBehaviour
    2. {
    3.     [SerializeReference]
    4.     ReferenceType refType;
    5.  
    6.     void Update()
    7.     {
    8.         Debug.Log("Type is " + refType.name + ". Should be Something");
    9.     }
    10. }
    The Property Drawer:
    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(ReferenceType))]
    2. public class ReferenceTypeDrawer : PropertyDrawer
    3. {
    4.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    5.     {
    6.         property.serializedObject.Update();
    7.  
    8.         if (property.managedReferenceValue == null
    9.         || ((ReferenceType)property.managedReferenceValue).name != "Something")
    10.             property.managedReferenceValue = new ReferenceType("Something");
    11.  
    12.         property.serializedObject.ApplyModifiedPropertiesWithoutUndo();
    13.     }
    14. }
    When I click play, the editor displays:
    HOWEVER, while playing, if select the game object to inspect it, the log changes to:
    It will stay the proper value until I run the game again, at which point the problem repeats.

    Anyone have any advice?
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Why do you use SerializeReference here? ReferenceType is just a normal class, so it'll work just fine with SerializeField. Unless you stripped out the part where you are subclassing an abstract base class.

    I guess the issue here is with readonly. As Unity deserializes the object, it will run the parameterless constructor. Then it will deserialize the fields. But .. oops, that field is readonly! Thus it cannot be deserialized and the value remains "Nothing". Until your property drawer replaces refType with a new object that gets initialized with "Something".

    Try removing the readonly modifier.
     
    NeedsLoomis likes this.
  3. NeedsLoomis

    NeedsLoomis

    Joined:
    Mar 22, 2017
    Posts:
    55
    Yup, the main project has an abstract base class. The idea is that, depending on the type set in the component, the drawer will display a different set of options pulled from it and its children in the inheritance hierarchy. An enum with inheritance basically.


    That did it! Excellent explanation too.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,494
    Keep in mind that you can use a read-only property instead. So you can have a serialized private backing field but only provide a getter in the property. So from outside the class you can't accidentally modify the name

    Code (CSharp):
    1. [SerializeField]
    2. private string m_Name;
    3. public string name => m_Name;
    It's also possible with an auto property:
    Code (CSharp):
    1. [field:SerializeField]
    2. public string name {get; private set;}