Search Unity

Editor: Serialized class values not writing after change in inspector

Discussion in 'Scripting' started by jwvanderbeck, Feb 16, 2018.

  1. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    This is really confusing me, especially given it is such a basic thing that I have done hundreds of times.

    Note: All of the code below is typed in by hand rather than copy/pasted so may contain typos. Some names below have been changed to protect the innocent and the company :)

    Problem: Only the initial values of a Serializable class are saved in the ScriptableObject asset. Any changes to those values in the inspector are not saved to the asset, though they do persist through assembly reloads. The values are lost upon a restart of Unity.

    Setup Detail:
    Code (CSharp):
    1. [System.Serializable]
    2. public class RandomFloatProperty
    3. {
    4.     public float minValue;
    5.     public float maxValue;
    6.     public string propertyName;
    7.  
    8.     // constructor that takes string and sets name
    9. }
    10.  
    11. public class PropertyPreset : ScriptableObject
    12. {
    13.     public List<RandomFloatProperty> Properties;
    14.  
    15.     [MenuItem("Assets/Create/Preset")]
    16.     public static void CreateAsset()
    17.     {
    18.         PropertyPreset asset = ScriptableObject.CreateInstance<PropertyPreset>();
    19.         asset.Properties = new List<RandomFloatProperty>();
    20.         RandomFloatProperty prop1 = new RandomFloatProperty("Scale");
    21.         prop1.minValue = 1;
    22.         prop1.maxValue = 2;
    23.         RandomFloatProperty prop2 = new RandomFloatProperty("Jitter");
    24.         asset.Properties.Add(prop1);
    25.         asset.Properties.Add(prop2);
    26.  
    27.         // this is just a helper method we use internally that handles the asset creation, don't worry about it.  It isn't important.
    28.         ProjectWindow.CreateAsset(asset, "DefaultPreset.asset");
    29.     }
    30. }
    31.  
    After creating an asset, I can look in the .asset file and see the proper values, including the property names, and the minValue and maxValue presets on the scale property. Additionally these show up properly as expected in the inspector, and the values from the .asset file are read properly. I tested this by modifying the asset file directly, and the value in the inspector updated accordingly.

    If I go into the inspector and change a property on a RandomFloatProperty, the value updates in the inspector and the value appears to be properly updated and retained. The updated value will persist through an assembly reload for example, including entering and leaving play mode.

    However, any of those changes made in the inspector never get written back out to the .asset file, so if you close Unity and re-open it all the values revert to the default!

    I have tried decorating every filed with [SerializeField] even though that should not be necessary with public fields, but that made no difference.

    I've been staring at this for about 4 hours now and nothing seems to make any sense. It should work, has worked in tons of other code, so I have no idea what I am missing.

    Can anyone help?
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Are you using a custom inspector to draw these objects? In that case, that might be broken.


    Other than that... It's very strange. Usually, when something fails to serialize, the values will be gone after an assembly reload. It sounds like Unity's able to serialize/deserialize the data, but not able to write it to file. Maybe there's a permission issue?

    It might also be that ProjectWindow.CreateAsset does something wrong. The file should be created through AssetDatabase.CreateAsset or AddObjectToAsset. Is that's what's happening behind the scenes?
     
  3. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    1) We had a custom inspector but to help debug this issue that is currently disabled, so this is using just normal Unity inspector.

    2) ProjectWindows.CreateAsset basically wraps up determing where int he project to create the asset and then calls the Unity internal method ProjectWindowUtil.CreateAsset which is a semi-hidden gem that essentially mimics the default behaviour you get when creating an asset in Unity. That means it creates the asset, and automatically puts the atist into rename mode.

    I highly doubt the method of asset creation is the problem as we use that all over and aren't having issues with other assets.

    After some more testing, it almost appears as if Unity simply isn't saving the ScriptableObject asset to disk at all and it has nothing to do with the properties list. I added just a simple float value directly to the ScriptableObject and that isn't being saved either.

    If I go into the OnValidate() method and force a AssetDatabase.SaveAssets() that fixes it but is insane overhead while modifying values so not a workaround really. Using EditorUtility.SetDirty in the same method dosn't seem to work.
     
  4. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Hmm, need more testing but setting it dirty in OnValidate may actually solve the issue. It doesn't appear to update the file on disk right away, but if I close Unity it does get update so maybe it is getting flagged and updated on exit. I need to do some more testing to be sure.