When you set “SaveGame” a value type (int, float, string etc), this value is saved. When you deserialize, you’ll get back this value.
When you set “SaveGame” a property that is a pointer to an object (Actor reference, Uobject reference… etc)
Only the pointer is saved, and at deserialization time, only the pointer is back, object is “relink” to your property if it exist but is never back at the state it was when serialized because it was not serialized.
Deep saving is the fact to save these objects
DtArchive wont deep serialize automatically all referenced objects that are tagged Savegame. Because :
– It take space in file and in memory.
– It cost to the processor.
– You don’t need to deep save an object that never change but sometime you’ll need to only save the reference. In this case you only tag SaveGame. Reverse case exist, where an object reference never change and you don’t need the savegame tag but need to deep serialize it.
In DtArchive, there is an interface called DtAIDeepSaver that allow you to deep save objets.
In this Map, there is some Door_Red_A3 and a Door_A3.
BP_Door_A3 is not IElement, neither have anything related to DtArchive currently.
The Door_red that is under the light is already settup as IDeepSaver and have as deep object the Door_A3.
In fact Door_A3 is already deepSaved in the default state of this map. That doesn’t work because the door’s state of the Door_A3 isn’t updated, so the bool is set to true if saved at true but once deserialized that is not reflected to the door.
– Open BP_Door_A3 and add the DtAISerialisable interface (we will see it in more details later) .
– Compile for change to take effect.
– add the following event : “event PostDeserialization” and connect an
“UpdateDoorFromIsOpen” node for update the door after deserialisation.
You should see that the red door under the light and the grey door are saved correctly.
– In BP_Door_A3 add an actor reference variable and call it deepObject.
– add the DtAIDeepSaver interface and compile.
– Open the function DefineDeepObject.
– Drag out the DeepSave array pin (output) and Make Array
– Drag out the first pin of the array and make DtADeepObject.
– Connect the variable deepObject to the object pin of the DtADeepObject .
– In viewport set the value of deepObject to the door_Red_A3 that is at the left of the grey one, it is half in the ground.
All in the map will be serialized because the red door half in the ground is deepSaving all three other objects on his left (already settup).
Now we have two elements :
– The lightswitch is an element (it save the state of the light).
– The red door under the light is an Element. All other objects (doors and staticMeshes) are deepObject of this Element.
You can (de)serialize from within the editor using construction script, don’t forget to use a filterElement node or a registerElement (for IElements) node for filter bad objects (else your editor will crash).
– Select the master and click on “Serialize all” in Instance Settings.
– Now move all objects.
– Select the master again and deserialize all.
Object are back at saved locations and state (because in general settings, Default applyTransform is set to world).
Apart from the lightSwitch, there is only one object that is Element in this configuration, it is the redOne under the light. All others are not IElement or have there ElementID in EditionMode (unactivated). DeepSaved objects doesn’t need an identifier because they are part of the save of the Element (we call this one the “Element root“).
Now you understand the difference between object and element, an element can contain more than one object.
Be carefull with that, if you Set two elements as valid IElement, they will be saved two times, once for each validated IElement, and each saving his deepObjects.
We won’t add check about that because that can be used in some case.
Cross references inside an element are handles and you’ll not have problem. In fact when an object is trying to serialize two times, first time is serialized, nexts are redirection only (no serialization, no events), datas can be access from all differents pathes (for a GetObject for exemple).
Crossing between different Elements is not controled and won’t be, if an object is part of two different elements, it will be saved two times, once in each element.
We need more test about that and need you too for experiment and found bugs if any.
You can deepSave an array of objects using an ArrayToObject Node, you can see that in the BP_Door_Red_A3. If object are in a Map, use GetKeys or GetValues and ArraytoObject.
If an object is null at serialization time, datas of this object and subObject are cleared. We will maybe add an option for change this behaviour.
If an object is null at deserialization time, it cannot be deserialized, it’s something we wanted to handle but we didn’t found how to give back the object to your property (we cannot get a ref to your pointer from BP, but we think about something like in the GetElementID BP implementation, a Get/Set system). Our deserialization process already can generate objects (used in a getObject node).
To handle this case you can use the event BeforeSubDeserialization of ISerializable.
It is called after the deserialization of the ISerializable object but before deserialization of his deepObjects.
If you have for exemple an array of N YourObjectClass that was generated and saved, when deserializing, you’ll have an array of N null object that cannot be deserialized.
Just do a foreachLoop and construct all object beforeSubDeserialization (An exemple in B5_Instances/WBP_PlayerList_B5).
In our exemple we are using deepSaving for save some actors, it’s not something bad but this feature is not here for that, his main goal is to deep save components, generated objects etc… something that is realy related to this blueprint or groupe of related blueprints. Else there will be one day you’ll don’t know where is what in your archives.
IElement is powerfull because they are traced by the Master and you keep real control of where is what and what is not existing anymore (with the lastLinkedDate).
– Don’t need Identifier (these are part of there element root).
– Are not shown in ArchiveSettings.
Don’t forget IElement.
You maybe ask yourself why there is a DtADeepObject struct needed between the object and the array, if you look at it there is an arrow for open an advance part which contain options.
These options are subject to change but the ApplyTransform will stay here
His behaviour can change and you need to experiment but it override all IElement Setting and is dispatch to sub-Deep if they are set to default.
Locks will be explain in the tutorial about ISerialisable.