BaseClass[], Can i easily fill it with Subclass instances in Editor [SOLVED]

It is late and i cannot wrap my head around this: Is there a quick built in way of supporting Class-Inheritance within the Editor?

I have a baseclass array that i want to fill via the editor

public DropAndPickupEffect[] inventoryTransitionEffects;

I want to insert different subclass-instances into this array, how do i get the Editor to show me some kind of subclass selection for each entry, do i have to write a costum editor, how would this work?
3078615--231707--upload_2017-5-22_1-29-5.png

this is my class + 2 example subclasses

[System.Serializable]
public    class DropAndPickupEffect {
    public    virtual void PickUpEffect( ActorController ac )    {    Debug.Log("Error, virtual method called"); }
    public    virtual void DropEffect( ActorController ac )    {    Debug.Log("Error, virtual method called"); }    
}

[System.Serializable]
public    class AttackReplacement: DropAndPickupEffect{

    public    string        attackID;
    public    AttackSlot    slot;

    public    override void PickUpEffect( ActorController ac ){
        Debug.Log("Invoked AttackReplacement");
        ac.ReplaceAttack( slot, GetAtt.ByID(attackID) );
    }
    public    override void DropEffect( ActorController ac ){
        Debug.Log("Invoked Reversion of AttackReplacement");
        ac.RevertAttack( slot );
    }
}

[System.Serializable]
public    class InvokeAttackOnDrop: DropAndPickupEffect{

    public    string        attackID;

    public    override void PickUpEffect( ActorController ac ){
        Debug.Log("Invoked InvokeOnDrop (does Nothing)");
    }
    public    override void DropEffect( ActorController ac ){
        Debug.Log("Invoked Reversion InvokeOnDrop");
        GetAtt.ByID(attackID).Invoke( ac.GetMortalObject(), Vector3.zero, new DynPos(Vector3.zero) );
    }
}

The problem here is that Unity has no way to edit arbitrary child classes. In order to add an element to this array from the editor, it would have to get a list of all classes that inherit from the base class, let you choose one, and present different fields depending on the type of the child class. Unity just doesn’t have the UI to do that.

Unity doesn’t do well with inheritance in general. It’s kind of a shame, I could be doing some things much cleaner but Unity often times doesn’t like to serialize, or show it in the editor, or some other thing. For example, I’d love to use interfaces to store a list of component references in the editor, but if I recall Unity just doesn’t know what to do with that. So you’ll just have to find a way to work around this.

1 Like

No, you don’t. Serialization is not polymorphic. I guess a way of doing this, is to use different arrays for different subclasses ONLY in the editor.

Thanks!

Yeah, that is what i am doing now, i have one array for each type of subclass, and just iterate through all of them. I could merge them into a single Array of the baseclass (since i only call the overwrite functions PickUpEffect and DropEffect). But i have to check if there is something like an “OnChange()” for Scriptable Objects so i can automatically do the merge if the Data changes. But in my case a Run-Time merge is no big deal either.

I can recommend FullSerializer, an open source serialization toolkit for Unity that supports this functionality well. GitHub - jacobdufault/fullserializer: A robust JSON serialization framework that just works with support for all major Unity export platforms.

umm… just make the DropAndPickupEffect a ScriptableObject… then make each subclass instance an asset. and it’ll work just fine… you don’t need any special serializer. and it supports Polymorphism just fine.

The idea is very similar to my Command Dispatcher system. Where a GameObject has a monobehaviour “CommandDispatcher” which has a SerializableDictionary <string,CommandDelegatorData>. CommandDelegatorData is an abstract scriptableObject with a single abstract function “Execute(SceneObjectCache data)”. SceneObjectCache is an abstract data container class that holds gameobject state for various project assets that use it. Similar to how the Animator Component holds state for Animator Controllers.

If you want to draw the instance inside the same inspector as the Monobehaviour, then that will take some Editor scripting to pull off. Similar to what my SerializableDictionary does, (which btw I need to update that post…). And in that post I’m using polymorphism. Its a SerializableDictionary<string,NamedInputData> where NamedInputData is an abstract ScriptableObject class but the dictionary is populated with subclass instances like AnyInputData, NamedAxisInputData, and NamedActionInputData.

2 Likes

Haha! Thanks, Why didn’t i see this? This is particulary funny for me because the inventoryTransitionEffects-Array already was on a ScriptableObject, i just had to go one deeper :smile:

here is how it looks now, BaseClass Array that stores Subclasses, baseclass inherits from Scriptable Object
3079268--231774--upload_2017-5-22_16-28-43.png

public    class DropAndPickupEffect : ScriptableObject {
    public    virtual void PickUpEffect( ActorController ac )    {    Debug.Log("Error, virtual method called"); }
    public    virtual void DropEffect( ActorController ac )    {    Debug.Log("Error, virtual method called"); }

    public    bool destroyOnDrop = false;

    public    AttackSlot slot = AttackSlot.None;
}

[CreateAssetMenu(fileName = "AttackReplacement", menuName = "ProjectSpecific/DropAndPickupEffect/AttackReplacement", order = 1)]
public    class AttackReplacement: DropAndPickupEffect{

    public    string        attackID;

    public    override void PickUpEffect( ActorController ac ){
        Debug.Log("Invoked AttackReplacement");
        ac.ReplaceAttack( slot, GetAtt.ByID(attackID) );
    }
    public    override void DropEffect( ActorController ac ){
        Debug.Log("Invoked Reversion of AttackReplacement");
        ac.RevertAttack( slot );
    }
}

[CreateAssetMenu(fileName = "InvokeAttackOnDrop", menuName = "ProjectSpecific/DropAndPickupEffect/InvokeAttackOnDrop", order = 1)]
public    class InvokeAttackOnDrop: DropAndPickupEffect{

    public    string        attackID;

    public    override void PickUpEffect( ActorController ac ){
        Debug.Log("Invoked InvokeOnDrop (does Nothing)");
    }
    public    override void DropEffect( ActorController ac ){
        Debug.Log("Invoked Reversion InvokeOnDrop");
        GetAtt.ByID(attackID).Invoke( ac.GetMortalObject(), Vector3.zero, new DynPos( ac.transform ) );
    }

    public    InvokeAttackOnDrop(){
        destroyOnDrop = true;
    }
}
1 Like