- Home /
Byte array of serialized data in MonoBehavior - changes not written to disk
Hello everybody.
I have a MonoBehavior implementing ISerializationCallbackReceiver. It contains a couple of things Unity can serialise and some Unity cannot, by default, serialise.
The problem real quick:
Saving the scene (via Ctrl+S) will not persist a byte array changed via custom inspector / Editor windows.
Force-Saving scene via
EditorApplication.SaveScene()
worksChanging something else on the same GameObject will also force Unity into persisting the byte array.
Assuming:
Unity has trouble recognising a changed by array?
I'm missing something important?
The problem in detail:
Here's a version of that class stripped down for brevity:
public class SequenceExecutor : MonoBehaviour, ISerializationCallbackReceiver {
[SerializeField]
private List<ValueField> globalDataContext;
[SerializeField][HideInInspector]
private byte[] serializedModelTree;
private UnityBtModel rootModel; // This is not serializable by unity as it contains deep tree structures further than 7 steps
[SerializeField]
private ExecutionType executionType;
public void OnAfterDeserialize() {
using (var ms = new MemoryStream(serializedModelTree)) {
ms.Position = 0;
this.rootModel = DataSerializer.DeserializeProtoObject<UnityBtModel>(ms.ToArray());
}
}
public void OnBeforeSerialize() {
this.serializedModelTree = DataSerializer.SerializeProtoObject(this.rootModel);
}
}
The important bit here is the byte array that is handled in the serializer callbacks. I'm editing this class via an EditorWindow that I open through a button added via custom inspector. Everytime OnInspectorGUI is called I set the instance of the class in the inspector dirty via EditorUtilities.SetDirty()
.
So all prerequisites for that byte array getting written to disk are there, to my understanding.
Here's what is happening though:
I'm changing the data in rootModel (add some children, change data inside the children) via the editor. Now I'm saving the scene (because that MonoBehavior is scene data). I have some Debug output that indicates I'm serializing (via the serialisation callbacks) the correct data and set it to the variable serializedObjectTree
, which is a serializable byte array. Now closing the project. When loading the project again, the changes did not persist.
However.
When doing the same thing but also changing something Unity can serialize "natively" - like the executionType
enum, then all changes, including the ones that go into the byte array, are persisted properly. In fact, it will already do to disable and enable the component in the inspector.
I wonder, what am I not getting here? Should I handle that byte array differently?
EDIT:
I also tried in OnInspectorGUI to force a new byte array down Unitys serializer like this:
var prop = serializedObject.FindProperty("serializedModelTree");
prop.ClearArray();
sequencer.OnBeforeSerialize();
for (int i = 0; i < sequencer.SerializedModelTree.Length; ++i) {
prop.InsertArrayElementAtIndex(i);
var element = prop.GetArrayElementAtIndex(i);
element.intValue = sequencer.SerializedModelTree[i];
}
EditorUtility.SetDirty(sequencer);
To no avail. It's still only writing the data (including the changed byte array) to disk only when I change something "natively" serializable.
For reference, here'S the inspector code. It's using the target object and passes it on to an editor window, where it gets edited. Edited changes are seen in the inspector.
[CustomEditor(typeof(SequenceExecutor))]
public class SequencerInspector : UnityEditor.Editor {
public override void OnInspectorGUI() {
if (target == null) {
return;
}
base.OnInspectorGUI();
SequenceExecutor sequencer = target as SequenceExecutor;
if (GUILayout.Button("Open Sequencer Settings")) {
var window = GenericPopupWindow.Popup<SequencerEditorWindow>();
window.SetData(sequencer);
}
var prop = serializedObject.FindProperty("serializedModelTree");
prop.ClearArray();
sequencer.OnBeforeSerialize(); // make real the byte array is up to date
for (int i = 0; i < sequencer.SerializedModelTree.Length; ++i) {
prop.InsertArrayElementAtIndex(i);
var element = prop.GetArrayElementAtIndex(i);
element.intValue = sequencer.SerializedModelTree[i];
}
EditorUtility.SetDirty(sequencer);
}
}
I'll see what else I can do in the meantime.
Okay, so forcing a scene save via EditorApplication.SaveScene()
does the job. But that can hardly be the solution. Shouldn't that be the same thing as doing File > Save Scene or Ctrl+S?
How is it that calling this in code works but using the save-shortcut or menu does not? I'm confused.
Answer by damagefilter · Mar 09, 2017 at 01:52 PM
It turns out that this was bound to Prefab instances most of the times.
In this particular case it would not suffice to just set the object dirty. Instead what needed to be done was to update the serialized version of said array and then mark the normal object representation dirty. That is one of the things I tried earlier but apparently I didn't do it the right way. So instead, here's how feeding data into the SerializedProperty of an array should look like... Apparently.
int currentArraySize = SerializedSequenceExecutor.FindProperty("serializedModelTree.Array.size").intValue;
SequenceExecutorObject.SerializeModelTree(); // Force update of the model tree data here
int newArraySize = SequenceExecutorObject.SerializedModelTree.Length;
if (newArraySize != currentArraySize)
SerializedSequenceExecutor.FindProperty("serializedModelTree.Array.size").intValue = newArraySize;
for (int i = 0; i < newArraySize; i++) {
SerializedSequenceExecutor.FindProperty(string.Format("serializedModelTree.Array.data[{0}]", i)).intValue = SequenceExecutorObject.SerializedModelTree[i];
}
EditorUtility.SetDirty(SequenceExecutorObject);
So there's that.
Your answer
![](https://koobas.hobune.stream/wayback/20220612090353im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Creating persistent object in Edit mode 1 Answer
How Mark Prefab Dirty? 1 Answer
Help with Missing Monobehaviours and Asset Serialization? 0 Answers
Set Dirty and Save Scene 1 Answer
[JS] [Editor] SerializedObject from a custom class. 0 Answers