Serialization of generic abstract class

I’m currently working on something like a custom variable management system.
I tried to make it as modular as possible, since I want to be able to scale the system in two different ways:

  • which datatypes I can represent with the system (e.g. bool, string, int and later on custom types)
  • How the effective value of the underlying datatype is stored… e.g. as a raw field, in a ScriptableObject, in a dictionary with an according key etc.

Therefor I have the following class, which I want to reference to in MonoBehaviours later on (the generic gives the scalability for datatypes)

[System.Serializable]
public abstract class BaseVariable<T>
{
    [SerializeField] private Variable<T> _variable;

    public void OnGUI(Rect position)
    {
        ... // Doing some stuff on how to store the variable
        _variable.OnGUI(position);
    }
}

WIth the corresponding class Variable. Again, the generic is the datatype I want to represent. The class Variable itself is abstract and can later on be implemented in different ways, as long as it can give me access to a value of the specified type. Those different implementations guarantee my scalability for the storage and access:

[System.Serializable]
public abstract class Variable<T>
{
    public abstract T Value {get; set;}

    public abstract void OnGUI(Rect position);
}

Those two classes have the following implementations. The datatype I want to store for now is bool and the storage / access of the value is a simple field which gets accessed by the required Value property:

[System.Serializable]
public class BoolVariable : BaseVariable<bool>
{ }

[System.Serializable]
public abstract class BaseVariableRaw<T> : Variable<T>
{
    [SerializeField] private T _value;
    public override T Value
    {
        get => _value;
        set => _value = value;
    }
}

[System.Serializabe]
public class BoolVariableRaw : BaseVariableRaw<bool>
{
    public override void OnGUI(Rect position)
    {
        Value = UnityEditor.EditorGUI.Toggle(position, Value);
    }
}

and to inspect everything I use a simple MonoBehaviour which holds my BaseVariable:

public class BoolVariableTester : MonoBehaviour
{
    [SerializeField] private BoolVariable _boolVariable;
}

which will call the following PropertyDrawer:

public abstract class BaseVariablePropertyDrawer<T> : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        UnityEngine.Object target = property.serializedObject.targetObject;
        BaseVariable<T> instance = fieldInfo.GetValue(target) as BaseVariable<T>;
        DrawInstance(position, instance);
    }
    public static void DrawInstance(Rect position, BaseVariable<T> instance)
    {
        instance.OnGUI(positon);
    }
}

[CustomPropertyDrawer(typeof(BoolVariable))]
public class BoolVariablePropertyDrawer : BaseVariablePropertyDrawer<bool>
{ }

Ooookay, so far so good. This system works with different kinds of datatypes and different kinds of implementations. I created such implementations for my bool datatype. I designed my OnGUI() method in BaseVariable so I can select between different implementations and then write them into the “Variable _varialbe” field of my “BaseVariable” class. In this case I only showed the implementation of the RawVariable, but there are (potentially) other access types too.
But now I have the problem that I can’t serialize that instance. I can’t even serialize my instance of BoolVariable in my BoolVariableTester class.
Any suggestions how I can make this work without writing my own serializer listening to ISerializeCallbackReceiver? I know I can use ScriptableObjects to some degree to serlialize abstract classes and even keep references to same instances, but I can’t seem to figure out how to exactly apply those techniques to my special case with multiple levels of abstraction.
I’d be really glad if somebody could help me with this!

Thanks in advance,
BOTHLine

Unity does not support inheritance / polymorphism for custom serializable classes. Fields are serialized based on the field type, not the actual class instance type. The thing is, Unity does not even store any type information for those classes, it just serializes the field data. Deserialization happens based on the type of the fields. So if you want / need such a setup your only two ways are:

  • using the ISerializationCallbackReceiver and some custom serialization format
  • using ScriptableObjects.

Note that when using ScriptableObjects the most important class that need to derive from ScriptableObject would be your “Variable” class since you only need polymorphism for this class, at least based on your example.

Though your whole setup seems to be not well designed and i don’t really see any advantage using such a setup. Note that generic classes do not represent any kind of polymorphism. A generic class with different types in the type parameter have absolutely nothing in common. So a List<int> and a List<string> are just as different as Vector3, DateTime or float. Generics just provide generic functionality for different implementations. Though those implementations are not compatible since the actual types are different.

Also note that your example classes could not be used at runtime in an actual build since you can not use the UnityEditor namespace in a runtime class without preprocessor directives which exclude those parts from the class. You should generally try to avoid mixing editor functionality with runtime code.