Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Problem with [SerializeReference] attribute and polymorphism

Discussion in '2019.3 Beta' started by MiriamTschanen, Oct 10, 2019.

  1. MiriamTschanen

    MiriamTschanen

    Joined:
    May 20, 2019
    Posts:
    4
    Hi,

    I'm having some trouble with the [SerializeReference] attribute. It seems that my objects are not stored correctly, whenever I reload a scene, I get lots of null pointer exceptions.

    I have the following script attached to a couple of GameObjects in the scene (toy example obviously):
    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using Classes;
    4.  
    5. [System.Serializable]
    6. public class SceneObject : MonoBehaviour
    7. {
    8.     [SerializeField] public ClassEnum classType;
    9.     [SerializeReference] public AbstractClass instance;
    10.  
    11.     void Start()
    12.     {
    13.         Debug.Log(instance.doSomething());
    14.     }
    15. }
    There's a custom editor for it that has a drop-down list for the 3 different types (A, B and C). Once chosen, an object of the corresponding type is instantiated.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEditor.SceneManagement;
    4. using Classes;
    5.  
    6. [CustomEditor(typeof(SceneObject))]
    7. public class SceneObjectEditor : Editor
    8. {
    9.     public override void OnInspectorGUI()
    10.     {
    11.         SceneObject sceneObj = (SceneObject)target;
    12.  
    13.         ClassEnum newType = (ClassEnum)
    14.                 EditorGUILayout.EnumPopup(
    15.                     "Class Type",
    16.                     sceneObj.classType);
    17.  
    18.         if (newType != sceneObj.classType)
    19.         {
    20.             switch (newType)
    21.             {
    22.                 case ClassEnum.A:
    23.                     sceneObj.instance = new ClassA();
    24.                     break;
    25.                 case ClassEnum.B:
    26.                     sceneObj.instance = new ClassB();
    27.                     break;
    28.                 case ClassEnum.C:
    29.                     sceneObj.instance = new ClassC();
    30.                     break;
    31.             }
    32.  
    33.             sceneObj.classType = newType;
    34.         }
    35.  
    36.         if (!Application.isPlaying && GUI.changed)
    37.         {
    38.             EditorUtility.SetDirty(target);
    39.             EditorSceneManager.MarkSceneDirty(sceneObj.gameObject.scene);
    40.         }
    41.     }
    42. }
    And here are the classes I'm trying to serialize:
    Code (CSharp):
    1. namespace Classes
    2. {
    3.     [System.Serializable]
    4.     public abstract class AbstractClass
    5.     {
    6.         public abstract string doSomething();
    7.     }
    8.  
    9.     [System.Serializable]
    10.     public class ClassA : AbstractClass
    11.     {
    12.         public override string doSomething()
    13.         {
    14.             return "I am A";
    15.         }
    16.     }
    17.  
    18.     [System.Serializable]
    19.     public class ClassB : AbstractClass
    20.     {
    21.         public override string doSomething()
    22.         {
    23.             return "I am B";
    24.         }
    25.     }
    26.  
    27.     [System.Serializable]
    28.     public class ClassC : AbstractClass
    29.     {
    30.         public override string doSomething()
    31.         {
    32.             return "I am C";
    33.         }
    34.     }
    35.  
    36.     [System.Serializable]
    37.     public enum ClassEnum
    38.     {
    39.         A,
    40.         B,
    41.         C
    42.     }
    43. }
    If I change the value with my drop down list and then hit play, I get all the correct messages. As soon as I reload the scene, I will get null pointers, until I use the inspector to select a different type again. What am I doing wrong?

    The confusing part is, I have a different script where I serialize a list of polymorphic types and it works just fine. I cannot for the life of me figure out what's different. Did something about SerializeReference change in the 0b6 update?
     
  2. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    1) see if your serialized scene file has the reference saved
    2) try to use SerializedObject for the custom inspector

    3) there are some issues if the base class is not an interface, maybe is that. send a bug report to Unity
     
  3. MiriamTschanen

    MiriamTschanen

    Joined:
    May 20, 2019
    Posts:
    4
    Fixed it!

    Apparently Unity seems to decide that objects without fields are not worth deserializing, so it just tosses them away. If I change ClassA as follows:

    Code (CSharp):
    1. [System.Serializable]
    2.     public class ClassA : AbstractClass
    3.     {
    4.         [SerializeField] public float hack;
    5.  
    6.         public override string doSomething()
    7.         {
    8.             return "I am A";
    9.         }
    10.     }
    Then it will carry over on scene reload just fine. This is also why my other example worked, those classes all have fields.

    I'll probably post a bug report about this later.
     
    M_R likes this.
  4. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    438
    Please do!
     
  5. LeonhardP

    LeonhardP

    Unity Technologies

    Joined:
    Jul 4, 2016
    Posts:
    3,136
    That would be much appreciated. Please post the issue ID here as well.
     
  6. jlreymendez

    jlreymendez

    Joined:
    Dec 11, 2012
    Posts:
    5
    Thanks for coming back with the fix. Did you repor the bug? Since I am still getting the same problem in 2019.3.10
     
  7. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    650
    Seems like it is still not fixed so here is a nicer workaround:
    [HideInInspector, SerializeField] bool hack;
     
  8. WonkyDonky

    WonkyDonky

    Joined:
    Aug 24, 2014
    Posts:
    65
    Is this now fixed? In what version?