Serialize different types in a list

Unity post-processing volume uses a profile to store all effects in a list, so I made a test like this:

    public class Test : MonoBehaviour
    {
        [Serializable]
        public class Effect
        {
            public bool enabled;
        }

        [Serializable]
        public class NewEffect : Effect
        {
            public float value;
        }

        [SerializeField]
        List<Effect> effects;

        [ContextMenu("Add New Effect")]
        public void AddNewEffect()
        {
            effects.Add(new NewEffect() { value = 1 });
        }
    }

But it doesn’t work. So why post-processing can do it?

Unity’s serialization engine is a bit odd in how it respects types, and it differs based on if it’s a UnityEngine.Object type, vs a general type.

So unity serializes by value for anything of a general class/struct type. It does not store the type information in the serialized data, but instead infers it on deserialization based on the type expected by the field it deserializes into. The benefit of this is that if you change the type in your code, as long as the shape hasn’t change, Unity will successfully deserialize. The down-side is that Unity does not respect polymorphism/inheritance when it serializes data. If your field/list is of type Effect, it serializes it as Effect, not as the actual ‘NewEffect’ you might implement.

There is an exception to this, and that is how it treats any class that inherits from UnityEngine.Object. These are serialized into their own asset data (either as a *.asset, inside a *.unity scene, or for special cases as the specific type it is). Then the serializer sticks in a guid that references that asset/object. Thusly serializing ‘by reference’. This is why you can have a field of type ‘Component’, and stick any and all component into that field and have it serialize correctly.

And if you look at the source, all PostProcessingEffectSettings inherit from this class which inherits from ScriptableObject which is a UnityEngine.Object:

1 Like

Unity’s serializer does not support polymorphism directly:

Thanks for your reply! So post-processing profile just serializes references in the list and stores data in some ScriptableObjects, which are strored in the same asset? If it is, how can we do it in a component? Make Effect inherit from Monobehaviour?

Well if you make effect inherit from MonoBehaviour than you’ll need to attach it to a GameObject to instantiate it.

ScriptableObject allows you to create unity objects that aren’t attached to GameObjects, so that may be your best bet.

Of course there are other options like serializing the data yourself.

I don’t know what would be best for you since I don’t know what you’re attempting to accomplish. But I’m willing to be ScriptableObject is going to be where it’s at.