- Home /
How to make a deep copy of a list of ScriptableObject derived types?
The structure of my game is pretty unusual, so I'll give some context first. I'm working on a little puzzle / maze game, where pretty much every object can be moved / modified.
I handle this in the following way: every one of my objects inherits from a base class called RotatableObject, which in turn inherits from ScriptableObject (is required to use polymorphism when serializing assets). Then, using the scene view and a custom mapEditorScript, I can add objects of my choice to a list rotatableObjectList.
When hitting play, I can play the puzzle and after stopping, the puzzle resets, which is what I want (I have a script that translates the rotatableObjectList into prefabs that it instantiates). The problem starts when I save the map. I use another ScriptableObject derived class called Map, that contains a list of RotatableObject, and some other puzzle / map specific information. using:
Map newMap = ScriptableObject.CreateInstance(); newMap.map = new List (rotatableObjectList); ... AssetDatabase.CreateAsset(newMap,path); for (int i = 0; i < newMap.map.Count; i++) { AssetDatabase.AddObjectToAsset(newMap.map[i], path); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh();
Saving and loading the map works, but after the map is saved, any changes that are made in play mode (by simply playing the game) are persistent, as the map ScriptableObject keeps being re-serialized. What I need is a to make a deep copy of the rotatableObjectList, and save that, so that the saved list is never changed, while the "active" in-game list can be.
I've looked at EditorUtility.CopySerialized(original, copy)
, but for this to work the original and the copy need to be of the same type, and I can't know the type of the RotatableObject derived class beforehand. Trying to find the type and then use it as a type (not as a variable) to create a copy object of the correct type led me to dynamic variables, which, if I understand correctly, are not implemented in Unity.
I can think of two other ways to make deep copies: one is to write a function that checks a given RotatableObject for the type by comparing it to all the possible derived classes in a long if else chain, and then returns an object of that type which can be used to copy the original object to.
Another solution could be to implement a Copy function manually in all the derived classes, which is responsible for creating a new instance of the class, giving in the correct parameters and then returning the new object.
Both of these methods seem error prone with respect to adding new objects, and involve writing extra code. So I was wondering, is there an easy way to make a deep copy of ScriptableObjects (of unknown type) that I'm not aware of? Or is there a clever way to use EditorUtility.CopySerialized(original, copy)
?
Thanks in advance!
Answer by Bunny83 · Apr 05, 2018 at 02:59 PM
Well in general you can create a copy of any UnityEngine.Object derived class (which includes ScriptableObjects as well) by using Instantiate. However it depends on what you understand by "deep copy". It will clone all serializable class instances that the scriptable object might contain. However it won't clone any objects referenced by the serialized object which are UnityEngine.Object derived themselfs. So if your scriptable object references a material, GameObject, [ ... ] or another scriptable object the clone will also references those objects. If you want to create an instance of those as well you have to instantiate them manually.
Your question doesn't contain much details about your class and which types are involved so it's impossible to give any more detailed advice. If you need more information you should include your class declaration in your question.
However it depends on what you understand by "deep copy". It will clone all serializable class instances that the scriptable object might contain. However it won't clone any objects referenced by the serialized object which are UnityEngine.Object derived themselfs.
This would be all I need. However, I was under the impression that Instantiate should not be used for creating ScriptableObjects, and that ScriptableObject.CreateInstance(Type type) should be used ins$$anonymous$$d. This is problematic for me as the type is not known beforehand, right?
No, it's perfectly fine to use Instantiate with scriptableobject instances. CreateInstance just has to be used ins$$anonymous$$d of "new". Every UnityEngine.Object has a native code counterpart which is responsible for handling tracking and serialization of the object. Unfortunately Unity is not very consistant in this area. Some UnityEngine.Object derived classes can be created with "new" (like $$anonymous$$aterial, $$anonymous$$esh and even GameObject), others have to be created with a dedicated method. ScriptableObjects can only be created with CreateInstance and Components can only be created with AddComponent. However all those instances can be cloned with Instantiate. This cloning is done on the native code side of Unity where the actual data is serialized / deserialized.
Even if you want to create a new scriptableobject without copying any values you can use GetType() on an existing instance and use the returned System.Type object in CreateInstance.
Thank you! That did the trick, and thanks for clearing up my confusion. It would never have occurred to me that instantiate could be used here.
Regarding using GetType(): I have not been able to figure out how it is supposed to work. I tried to use something like:
Type objectType = GetType(object); var newObject = ScriptableObject.CreateInstance(objectType);
where the object is also a Scriptable but I would always get the error "objectType is a variable but is used like a type" (even when using $$anonymous$$akeGenericType in between). Am I missing a step here?
Your answer
Follow this Question
Related Questions
ScriptableObject : Serialized Polymorphism Classes Can Not be Deserialize with Polymorphism 1 Answer
ScriptableObject asset loses reference after restarting Unity 1 Answer
[Solved]How to serialize Dictionary with Unity Serialization System 6 Answers
Difference between assigning a value in inspector and with a custom editor script 1 Answer
Serializing ScriptableObject into scene without writing to asset 1 Answer