Tagged: bug scriptableobject references
In some of my Bound graphs I loose reference to my ScriptableObjects after Unity recompiles.
This problem doesn’t happen on all my Graphs and not even on all ScriptableObjects in the same graph.
I included a screenshot of a simple graph in which this happens. In the left most C# Event I sometimes loose my reference to Intro02. In the right most method I just lost my reference to another ScriptableObject.
I only have seen this happen to references to ScriptableObjects. Everytime I saw this happen was in a Bound graph which was not a prefab. This happens on arbitrary changes to my code (so I don’t change anything to the ScriptableObject). I deleted the graphs and remade them, but that also didn’t change anything.
If I fix the reference and save the Scene, then trigger a recompile, I will have lost my references again. If I then discard my changes in the scene I get my references back.
I now also have the problem on references to GameObjects
Edit: I’m on version 2.9.4 and Unity version 2019.1.9f1
I found a method to reproduce this bug in a minimal case.
I included a small Unity project. In this project is one scene, in this scene is one Bound Flow Graph, with one Node referencing the ScriptableObject ‘A’. The steps to reproduce this bug:
1. Drag the ScriptableObject ‘B’ through ‘F’ in here and select the Method Trigger
2. Save the Scene.
3. Change something in coo.cs that triggers a recompile.
4. Check if one or several of the Nodes lose their reference to the ScriptableObject. You have reproduce the bug. Otherwise continue with step 5
5. Delete the Nodes ‘B’ through ‘F’ from the graph
6. Save the Scene
7. Restart Unity.
8. Go back to step 1 (everytime I tried this, the bug triggered the first or second time).
Once you have triggered the bug, it is persistent. If you try to add the reference back, save and recompile, the reference is always removed.
After I trigger the bug, it can sometimes be solved by restarting Unity (but not always). Until now, everytime I deleted the Library directory and rebuild the Asset Database the bug will not be triggered.
I think I traced the bug to the source.
In Graph.cs Serialize() is called after I recompile.
If I look at the list of _objectReferences if the bug occurs, the references that are lost are “null” (not null, but the Unity “null”).
The name field of the “null” reference as the error: UnityEngine.MissingReferenceException: The object of type ‘Condition’ has been destroyed but you are still trying to access it.Your script should either check if it is null or you should not destroy the object.
I’m not sure how Unity works exactly during recompile, but my colleague informs my that during recompile ScriptableObjects that are not referenced can be temporarily destroyed. I’m not sure if this is what triggers this behaviour, but it could be.
At least I’m pretty confident this is the moment and location where this bug is triggered.
I managed to make a dirty fix. Which perhaps breaks more, but it might give some direction for the real fix:
1. In Graph::Serialize I store a copy of the _serializedGraph and _objectReferences
2. In Graph::OnValidate I restore the copy to _serializedGraph and _objectReferences (at this point Unity serialization has resolved the “null” reference) and call Deserialize()
3. Also in Graph::OnValidate I set a flag to true
4. In Graph::OnEnable I set this flag to false
5. In GraphOwner::Validate I check if the flag is true, if so, I copy graph._serializedGraph to boundGraphSerialization and graph._objectReferences to boundGraphObjectReferences
This is quite a dirty fix and I don’t know if I handle all the serialization cases. But what it does is:
During OnBeforeSerialize Unity has not resolved all references yet, resulting in Graph to lose its reference. I store the unresolved references at this point.
During OnValidate Unity has resolved all it’s references. I restore the serialized data to graph and Deserialize. Effectively restoring the graph to its correct state.
GraphOwner tries to Deserialize the Graph also, using the corrupt serialized data. So in the case this is triggered, I give graphOwner the correct data, just before it serializes.
I think for the correct why to fix this, is not handling reference in the OnBeforeSerialize state, but wait until the Validate state (or another state) and handle the serialization then.
EDIT: In my example scene this worked. In the large project (which also uses prefabs) references are still lost