Inheritance and Lists

I’ve got a scriptable object containing a list, where the elements will actually be derived classes of the list’s type.

class S : ScriptableObject {
    public List<A> list;

    public void Add(A a) {
        list.Add(a)
    }
}

abstract class A {}  //standalone class, not monobehaviour or SO
class B : A {}
class C : A {}

I’ve created an instance of the scriptable object, and the list of that asset is extended from a separate script (as part of an Editor script, if that matters?):

class E {
    //...
    S so;
    so.Add(new B());
    so.Add(new C());
    //...
}

Finally, there’s a monobehaviour which the asset is attached to, and I want to access the list of that asset.

class B : MonoBehaviour {
    [SerializeField] S so;

    void Start() {
        foreach (var a in so.list) {
            if (a as B) { print("B"); }
            else if (a as C) { print("C"); }
            else { print("A"); } // <-- this is all that gets printed
        }
    }
}

Essentially, by the time I am accessing the list in the monobehaviour, the derived information about the objects in the list seem to have been lost, and they can’t be cast to their actual types.

What am I doing wrong? Is there a way I can make sure the objects in the list stay as their actual types, rather than being converted to their base class?

Hi Jonny,

In which course and which lecture are you?

Hi Nina,

This isn’t related to any particular lecture - I’ve finished all the currently-produced content on the RPG course and this was just an issue I ran into when I was working on some new features while Sam & Rick re-master it.

Fortunately, some in-depth Googling has helped me find the issue: Unity’s serialization doesn’t support inheritance (except from MonoBehaviours or ScriptableObjects) [related answer from Unity forums]. Basically, when the ScriptableObject asset is saved, it can’t deal with inheritance in List<A> and any derived classes in this list are saved only as their base class.
The solution I’ve had to add is as follows:

class S : ScriptableObject {
    public List<B> listB;
    public List<C> listC;
    public List<A> list {
        get {
            var list = new List<A>();
            foreach(B b in listB) {list.Add(b);}
            foreach(C c in listC) {list.Add(c);}
            return list;
        }
    }

    public void Add(A a) {
        if (a as B) { listB.Add(a); }
        if (a as C) { listC.Add(a); }
    }
}

This way, all added objects are saved with all their relevant data, and the combined list is created as a property whenever required. This might end up being a pain if there are lots of different sub-classes, but for now it’s not too unbearable.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms