I’ve been playing with serialization for a while, and most things are going great. However, I’ve hit one case where (hopefully) I’m missing something silly.
I have a class derived from ScriptableObject. It in turn has a child class. I create an instance of the child using ScriptableObject.CreateInstance(childClassType) and adding the reference to a List<>.
Both my custom editor and the inspector show everything working as intended, but as is the case when there’s a serialization issue when I re-open Unity it’s lost.
What’s annoying is that I’ve got what I’m sure is the exact same pattern working in another project. The only difference is that here I am creating everything as an asset, where there I am creating objects in a scene. In this case the objects can’t be a part of a specific scene, so I hope that difference isn’t the cause. I have heard of people having similar issues in the past, but as far as I can tell everyone has solved it by using ScriptableObject, which I am already doing…
Has anyone used ScriptableObjects with class inheritance and successfully made assets using this?
Edit for future readers: Skip right on down to this post for my solution. Much to my pleasure this issue was neither a bug with my code nor with the serialization system, and could ultimately be fixed in about 15 seconds.
What is holding that list? Is the list properly serialized in the scene?
After creating your asset… Do you call AssetDatabase.SaveAssets(); ?!
Maybe that is the problem.
I believe so. It’s serialised the same way I’m serialising a number of other lists that are all working.
Hmmm… I’d like to say yes, I don’t specifically remember doing that, so perhaps that’s it.
Cheers guys, I’ll let you know how I go when I next get to that project.
Nope, didn’t help. I actually wasn’t calling that anywhere except when I pressed the “save” button I’ve stuck on my custom editor.
An object is getting deserialized, it’s just being reverted to it’s parent type.
Something that seems odd to me is that I’m also calling ImportAsset on the parent asset immediately after adding the sub-object to the asset. I don’t understand why that doesn’t break it immediately, because if it’s re-loading it after it’s saving it then surely I should have the same problem?
Edit: I wonder if it’s something to do with how I’m creating the ScriptableObject instance. I’m doing this:
ParentClass asset = ScriptableObject.CreateInstance(childType) as ParentClass;
if (AssetDatabase.Contains(mainAsset)) {
AssetDatabase.AddObjectToAsset(asset, mainAsset);
// Saving then re-importing...
}
else {
Debug.LogError("Main asset doesn't exist"); // I've never seen this error appear
}
// Elsewhere...
parentClassObject.assetList.Add(asset);
AssetDatabase.SaveAssets();
As far as I understand ScriptableObject is meant to handle the inheritance, and it manages just fine in other cases.
For what it’s worth, if I make ParentClass abstract I get null references rather than empty parent class objects.
I guess I’ll just have to pore over the whole thing closely to see what I’m missing.
Another edit: I’m not accessing/changing things through SerializedObject in this case. That doesn’t seem to be a requirement elsewhere, but maybe I should give it a go, or need to somehow mark the assets as dirty. (Update: SetDirty didn’t help. Will try converting to use SerializedObject.)
Gah! The forum just list a really detailed post!
In short, I made a from-scratch replication of my system doing just the saving/loading part that wasn’t working. Going overboard with EditorUtility.SetDirty(…) and saving/re-importing the assets for every change seems to have nixed the issue.
I was worried that there might be a snag saving derived/abstract/something ScriptableObjects as assets, but this exercise ruled that out. Now I’ve just got to spot what I’m doing differently here compared to my other code where it’s not working. Yay!
So…
This still isn’t working. But here’s some crazy.
- I have an abstract class that inherits ScriptableObject.
- I have a concrete class that inherits from that.
- I have a List<…> of said concrete class in another object derived from ScriptableObject.
- Using my custom GUI I create add an instance of the concrete class to the list.
- I close and re-open Unity. The list is populated by objects reporting that they are of the abstract class.
- I open one of the above code files, insert a space in some white space, and hit save.
- I switch back to the Unity Editor. The script is recompiled. The list immediately shows contents of the concrete type, including whatever data I put in there.
Ergo, the data is being serialized correctly, and something else is going wrong.
I had actually seen 7 before, but never realized that it was correct data from serializations from prior Editor sessions.
Wow.
Found it.
The issue was with… the name of my abstract class’ source file.
It didn’t match the name of the class inside it. I accidentally had the last part of the name written twice, a-la “AbstractClassImplementationImplementation”. Both my Project window and the file tabs in my IDE are too thin for me to see this under normal circumstances, and while MonoBehaviours warn you about this ScriptableObjects apparently do not.
I’ve no idea why it correctly deserialized after a recompile. My guess is that it knows the class name in that case, so it uses that rather than the file name, but who knows? In any case, due to that inconsistency I was making a bug report about how my test case worked but doing the exact same thing in the same project with different classes did not. In doing so I copied the class name and boom - all these extra letters got pasted. I stretched the Project window out and immediately found the issue.
The good news is that when I come home tomorrow I get to finish the the thing I was making. 