Unity's serialization system doesn't normally support inheritance. Using the [SerializeReference]
attribute allows it to do so, but Unity doesn't have an inbuilt drawer for those fields so they appear blank by default and if you assign a value to them in a script they only display that value without anything in the Inspector to change to the other types that could be assigned to the field. This means they normally aren't that useful without writing a custom Property Drawer for them, but Animancer's PolymorphicDrawer
(which is used for Serialized References to Transitions) can also be used on your own types.
Regular | Polymorphic |
---|---|
The [SerializeReference] field appears, but has no way for you to assign a value to it. |
There are two main ways to use the PolymorphicDrawer
:
- Have the field type implement
IPolymorphic
. That interface doesn't actually contain any members, it simply indicates that the type should use this drawer. - Give the field a
[Polymorphic]
attribute. That can be done without modifying the type, but means you need to apply it to every field rather than just once to the type.
You can also Inherit from PolymorphicDrawer
to implement other custom features (that's what TransitionDrawer
does).
Example
Consider the following script with several classes in it (the script must be named PolymorphicExample.cs
to match the MonoBehaviour
class at the bottom):
using Animancer;
using System;
using UnityEngine;
An abstract
base Item
class with fields that all items have:
[Serializable]
public abstract class Item
{
[SerializeField] private string _Name;
[SerializeField] private string _Description;
[SerializeField] private float _Weight;
}
Several more classes which Inherit from Item
to add their own more specific fields:
[Serializable]
public class Armour : Item
{
[SerializeField] private int _Defence;
}
[Serializable]
public class Potion : Item
{
[SerializeField] private int _HealingAmount;
}
[Serializable]
public class Weapon : Item
{
[SerializeField] private int _Damage;
[SerializeField] private float _AttackSpeed;
}
And a MonoBehaviour
class with several different fields to show how the serialization system works:
public class PolymorphicExample : MonoBehaviour
{
[SerializeField] private Item _ItemSerializeField;
[SerializeField] private Potion _PotionSerializeField;
[SerializeReference] private Item _ItemSerializeReference;
}
Regular | Polymorphic |
---|---|
The code exactly as presented above. | public abstract class Item : IPolymorphic on the top class or [SerializeReference] [Polymorphic] private Item itemSerializeReference; on the bottom field. |
_ItemSerializeField doesn't show up at all. A regular serialized field doesn't support inheritance so the field could only be serialized as an Item , but that class is abstract so a base Item can't actually exist and Unity doesn't bother trying to serialize it at all. In very old versions of Unity (back around Unity 3.0) attempting to deserialize this would actually crash the Unity Editor. |
|
_PotionSerializeField shows up properly, but will always be a Potion and can't be any other type of Item . |
|
_ItemSerializeReference shows up, but it's null by default and Unity doesn't draw anything to let you assign a value to it in the Inspector. |
_ItemSerializeReference shows a dropdown menu which lets you choose which type of Item you want to assign to the field before configuring its values. The menu automatically includes every serializable type that could be assigned to the field. |
Being able to assign any type of Item
without specifying which one in code means you could create an inventory array and configure it in the Inspector:
public class PolymorphicExample : MonoBehaviour
{
[SerializeReference] private Item[] _Inventory;
}