different results for polymorphism in unityScript and inspector

hi,

Im getting different results with unity script by itself, and when i use custom inspector script.

[
(Note : the simplest way is to check the attached unitypackage and see the code its much faster to understand what my problem is )

[3894-test.txt|3894]

Please rename the file to test.unitypackage, and import into an empty project to check it out, i’ve written down the comments that you can easily understand.

                                      or

you can also check the file from dropbox at
https://www.dropbox.com/sh/i21xxwrodjz1qkk/2mP0bjmVqd
]

Here’s the problem description :

  1. i have created a base class, and a derived class of the base class.
  2. create a baseclass array, and initialize it with a derived class object
  3. try to call a function to see which class’s implementation is called
  4. first result is absolutely fine, as expected the result is derived function is executed
  5. now, if i move the instantiation code to custom gui Inspector, and try to run the code then , i see that the base class’s function is called!

Which is very weird to me, may be its very silly thing that im doing here …
please correct me where im going wrong with this

Also if you did not understand any part of my description or anything in specific please let me know

Also im attaching the inspector only files, so you can test it better
[3899-test_inspector_only.txt|3899] (rename the extension to unitypackage)… you only would need to get the inspector code to produce the output to “Derived class foo”

Thanks

testClass script:

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

public class testClass : MonoBehaviour
{
    public baseClass[] baseClassArray;// = new baseClass[1];
    void Start ()
    {
        //1. run and check the debug log in unity ... you'd see
        // derived class foo 

        //2. comment below two lines and UNCOMMENT AS MENTIONED STEP 3 AT 'CUSTOMINSPECTOR.CS ... LINE 19
        //baseClassArray = new baseClass[1];
        //baseClassArray[0] = new derivedClass();

        //3. now run again and check the output log in unity ... you'd see
        // base class foo 
        
        //baseClassArray[0].foo();

  	}
    
}

//[System.Serializable] //**Commenting this out will give you your expected behavior.
public class baseClass
{
    public baseClass()
    {
        
    }

    public virtual void foo()
    {
        Debug.Log("base class foo");
    }
}

[System.Serializable]
public class derivedClass : baseClass
{
    public derivedClass()
    {

    }

    public override void foo()
    {
        Debug.Log("derived class foo");
    }
}

customInspector script:

using UnityEngine;
using UnityEditor;

using System.Collections;

[CustomEditor(typeof(testClass))]
public class customInspector : Editor
{
    private testClass obj;
    private SerializedObject _object;
    
    
    void OnEnable()
    {
        _object = new SerializedObject(target);
         obj = (testClass)target;
    }

    //3. uncomment the below function fully ................... from line 20 to line 31
    public override void OnInspectorGUI()
    {
        _object.Update();

        if (obj.baseClassArray == null || obj.baseClassArray.Length == 0)
        {
            obj.baseClassArray = new baseClass[1];
            obj.baseClassArray[0] = new derivedClass();
            _object.ApplyModifiedProperties();
        }

        obj.baseClassArray[0].foo();

        DrawDefaultInspector();
    }
   
}

I’ve just experienced a similar situation: MonoBehaviour has a generic list of type BaseClass; insert elements of type ChildClass. So far so good, but when you press play, the elements of the list are now of type BaseClass! :confused:

My solution: not only does BaseClass need to be marked as Serializable, but it also needs to inherit from ScriptableObject. Instead of a constructor, you should implement your initialization code in OnEnable after checking for nulls.

The default serializer / deserializer ‘shears’ the instances of ChildClass to plain old BaseClass (even if BaseClass is abstract… ).

The ScriptableObject serialization mechanism is smarter. For example, if you have two references to the same object, the default deserializer will make multiple copies, while a ScriptableObject will make a single object with correct references.

I learned all of this from the wonderful ‘Serialization Best Practices Megapost’ on the forum. The only aspect of that post that didn’t work for me were the hideFlags settings. ymmv

[Serializable]
public abstract class BaseClass : ScriptableObject {
    
}

[Serializable]
public class ChildClass : BaseClass {
    public void OnEnable() {
        // do your initialization here after checking if this object isn't the result of deserialization
    }
}