'Unity: Assign instance of abstract object in the inspector

In my game, I have a magic system that uses a command-pattern to store the steps. How can I assign an abstract command not derived from MonoBehavior or ScriptableObject in the inspector (or just not hard-coding it)?

Below I have the minimum example, note that SpellCommand does not derive from MonoBehavior nor ScriptableObject.

public abstract class SpellCommand {
    public SpellCommand (int power = -1) {
        this.power = power;
    }
    public abstract void Cast();
}

And here is what a spell might look like

public class SampleSpell : SpellCommand {
    public SampleSpell(int power) : base(power: power) { }

    public override void Cast() {
        Debug.Log("Sample Spell Cast");
    }
}

And finally a MonoBehavior to invoke it

public class TestSpellInvoker : MonoBehaviour
{
    public SpellCommand testSpell;

    void Start() {
        testSpell.Cast();
    }
}

Since I know Unity doesn't support serialization of abstract classes not derived from MonoBehavior, I wrote my own inspector script to assign and display the contents of testSpell but whenever I enter play mode, it seems to be nulled out.

I've considered removing all polymorphism and simply using a large Enum to represent subclasses, but that would introduce a ton of complexity and it feels very much against the spirit of C# and OOP. I've also considered using ScriptableObjects, but that would require (in my case) having potentially thousands of ScriptableObject instances saved as files that I would then have to wrangle (although that being said my knowledge of ScriptableObjects is pretty elementary so I might be missing something).



Solution 1:[1]

  1. Your abstract class and implementors should be Serializable. If it's not, your data won't be stored either in scene, or in a prefab.
  2. I haven't used it in a MonoBehaviour but this works in a ScriptableObject. Use SerializableReference instead of SerializableField. As long as you follow the rules in the documentation (Serializable, not from UnityEngine.Object, not a value type, not a dictionary), then you can store the implementors instances.

NOTE: If you do this, you'll need to adjust your custom inspector to display the data depending on the implementor.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tricko