Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

ScriptableObject deserialization from scene asset

Discussion in 'Scripting' started by rorydriscoll, Feb 29, 2016.

  1. rorydriscoll

    rorydriscoll

    Joined:
    Apr 14, 2015
    Posts:
    23
    [EDIT] I've attached a minimal test project [/EDIT]

    I'm pretty doubtful that I'll be able to get help on this one, but here goes anyway...

    I have a bunch of scriptable objects (nodes for a graph) that are save to the scene asset file (i.e. they are not saved in the asset database). I've verified that they serialized correctly by opening up the file and checking that all the objects are in there, and that the reference file ids match.

    I'm sure I'll get this question, but yes, I need scriptable objects since there are potentially circular references in the graph. I also need to save the objects in the scene since they need to reference scene gameobjects to perform their actions.

    Code (csharp):
    1. [Serializable]
    2. public class Transition
    3. {
    4.     public State target;
    5.     public Condition condition;
    6. }
    7. public class State : ScriptableObject, ISerializationCallbackReceiver
    8. {
    9.     public List<Transition> transitions;
    10.     ...
    11. }
    12. public class StateMachine : MonoBehaviour, ISerializationCallbackReceiver
    13. {
    14.     public List<State> states;
    15.     ...
    16. }
    My issue is that on editor load, these scriptable objects are never deserialized. I have logging in OnEnable, OnDisable, OnDestroy, OnBeforeSerialize and OnAfterDeserialize but none of them fire. Accordingly, when I view the object in the inspector, it is missing (null). I do get the deserialize message for the state machine container, but this is a MonoBehaviour:

    After Deserialize StateMachine

    Now the strange thing is that if I make a code change, which forces the dll to reload and also a full deserialization, then the scriptable objects suddenly appear.

    After Deserialize BooleanCondition
    After Deserialize StateMachine
    After Deserialize State
    After Deserialize State
    Enable Condition
    Enable Condition
    Enable a
    Enable a
    Enable b
    Enable b

    What I'm trying to work out, is why the object deserialization was skipped when the editor first loaded. Running the game produces the same effect. All of the scriptableobjects are created with the default hide flags. I've also tried using DontUnloadUnusedAsset in case they appeared to be unreferenced after load, but this made no difference.

    Given that the objects appear after reload, and that they're only present in the scene file (i.e. I restarted the editor to make sure they weren't lying around anywhere), that tells me that they were in fact deserialized from the scene asset, but they weren't fully instantiated for some reason. Can anyone give me insight as to why this might be?
     

    Attached Files:

    • Test.zip
      File size:
      18.7 KB
      Views:
      337
    Last edited: Feb 29, 2016
  2. rorydriscoll

    rorydriscoll

    Joined:
    Apr 14, 2015
    Posts:
    23
    One other tidbit... I noticed that the default inspector displays the state names even when the object references are null. Looking at the code for ObjectField, it uses an internal call (objectReferenceStringValue) to get the string to display. This furthers my thought that the engine has correctly deserialized the objects internally.

    So I think that now I'm pretty much stuck, since the information I need is in the black box that is the Unity native code.
     
  3. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    I've never really seen the ISerializationCallbackReceiver applied to a UnityEngine.Object before. Isn't it primarily for serializable classes/structs?
     
  4. rorydriscoll

    rorydriscoll

    Joined:
    Apr 14, 2015
    Posts:
    23
    It works for both MonoBehaviour and SerializableObject.
     
  5. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    That's interesting. I've only used it on non UnityEngine.Objects. But other than submitting a bug report I would think you could work around ISerializationCallbackReceiver via OnValidate. Perhaps just something like this..
    Code (csharp):
    1.  
    2. void OnValidate(){
    3. var self = (ISerializationCallbackReceiver)this;
    4. self.OnBeforeSerialize();
    5. self.OnAfterDeserialize();
    6. }
    7.  
    that doesn't really solve the issue of loading outside of the editor (if its an issue outside of the editor?)
     
  6. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    Also, i just took a look at the test. Unless unity changed something, i believe the class for State needs to be in its own .cs file seperate from the behaviour.

    Unless something changed, I don't think that unity can serialize a script without it having its own GUID (by being in a different file)
     
    rorydriscoll likes this.
  7. rorydriscoll

    rorydriscoll

    Joined:
    Apr 14, 2015
    Posts:
    23
    That indeed fixes the problem! I didn't realise that there is such a requirement for scriptable objects. It's still a bit peculiar that the serialization was partially working though. Thanks for your help!
     
  8. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    No problem, that is a bit confusing that ScriptableObject.CreateInstance didn't throw up. from working with scriptable objects in the past (and all the hideflags magic) I highly advise you to take memory profiles in the profiler to see how many instances are actually alive in the editor. (thats when you go to the profiler, select the memory row on the left side, switch it from simple to detailed and hit Take Sample: editor). You'll then be able to see the count of instances (to verify they are not leaking). They should be under Scene Memory category (sub category MonoBehaviours) if its saving with the scene. otherwise they will be in Assets or Not Saved.
     
  9. rorydriscoll

    rorydriscoll

    Joined:
    Apr 14, 2015
    Posts:
    23
    Good idea. I'm guessing that they actually were referenced by the scene since they made it into the disk asset, but it's good to know where to look for these kinds of things.
     
    bdev likes this.