1

I created a custom Inspector for some of my ScriptableObject. The inspector seems to work perfectly. If a value gets changed in the inspector, this value is used in game. But as soon as I restart Unity the value is back on its old value, so the changed are not applied to the file. I have checked that by looking in the file. Here is my custom Inspector:

[CustomEditor(typeof(ScriptableObjects.OBJ))]
public class OBJEditor: UnityEditor.Editor {
    public override void OnInspectorGUI() {
        _ = DrawDefaultInspector();

        ScriptableObjects.OBJ obj = (ScriptableObjects.OBJ) target;
        obj.Offset = EditorGUILayout.Vector2Field("Offset", obj.Offset);

        ...

        EditorUtility.SetDirty(obj);
    }
}

I have added the line EditorUtility.SetDirty(bc); because I have read that this line should apply changes to the file. The ... stand for some other lines that are pretty much the same as the two lines above the dots.

How can I save my changes to the ScriptableObject file.

Timisorean
  • 1,388
  • 7
  • 20
  • 30

4 Answers4

3

Do not directly change values through the target at all!

The manually handling of setting dirty and Undo/Redo is complex and unnecessary!

Allways use the built-in serializedObject and go through SerializedProperty

[CustomEditor(typeof(ScriptableObjects.OBJ))]
public class OBJEditor: UnityEditor.Editor 
{
    private SerializedProperty offset;

    private void OnEnable()
    {
        // Link the properties
        offset = serializedObject.FindProperty("Offset");
    }

    public override void OnInspectorGUI() 
    {
        DrawDefaultInspector();

        // Load the real class values into the serialized copy
        serializedObject.Update();

        // Automatically uses the according PropertyDrawer for the type
        EditorGUILayout.PropertyField(offset);

        ...

        // Write back changed values and evtl mark as dirty and handle undo/redo
        serializedObject.ApplyModifiedProperties();
    }
}
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • `EditorGUILayout.PropertyField(offset);` gives me a `NullReferenceException: Object reference not set to an instance of an object`. I think `OnEnable()` doesnt get called. – Timisorean Sep 16 '19 at 15:16
  • `OnEnable` is called (you can find out using Breakpoints) .. since I don't see the implementation of your `OBJ` class I assumed you'll have a `public Vector2 Offset;` or `[SerializeField] private Vector2 Offset;` the name has to match exactly and it has to be a serialized field .. it will not work if it is a property. – derHugo Sep 16 '19 at 15:21
0

It is considered best practice to cache your casted 'target' on the OnEnable method of the inspector. You also have a typo, you're setting dirty an object variable called 'bc' while your target variable is 'obj', you also are setting it dirty every draw call, this will keep dirtying your project all the time, preferably dirty it when something actually changed.

[CustomEditor(typeof(ScriptableObjects.OBJ))]
public class OBJEditor: UnityEditor.Editor 
{
    ScriptableObjects.OBJ obj = null;
    protected void OnEnable()
    {
        obj = (ScriptableObjects.OBJ)target;
    }
    public override void OnInspectorGUI() 
    {
        serializedObject.Update();

        _ = DrawDefaultInspector();

        EditorGUI.BeginChangeCheck();
        obj.Offset = EditorGUILayout.Vector2Field("Offset", obj.Offset);

        ...
        bool somethingChanged = EditorGUI.EndChangeCheck();
        if(somethingChanged)
        {
            EditorUtility.SetDirty(obj);
        }
        serializedObject.ApplyModifiedProperties();
    }
}
ChoopTwisk
  • 1,296
  • 7
  • 13
  • `serializedObject.ApplyModifiedProperties()` only makes sense if you are changing the properties of the `serializedObject` which is not the case in your code... – derHugo Sep 16 '19 at 03:07
  • I know, but its just in case something changes in 'DrawDefaultInspector'. – ChoopTwisk Sep 16 '19 at 08:34
0

You need to pass the correct object to mark as dirty to EditorUtility.SetDirty();. Currently you're passing bc but you need to pass obj, because thats how you named it in your code. This will mark your ScriptableObject as dirty and once you select File > Save Project, it will be saved.

So change EditorUtility.SetDirty(bc); to EditorUtility.SetDirty(obj);

  • Oh sorry, I forgot to change the name. But unfortunately, that's not the problem. Its just a typo in my question. – Timisorean Sep 15 '19 at 21:11
0

I had the same issue and it required me adding the keyword

[CanEditMultipleObjects]

to the top of the class definition.

mrVentures
  • 121
  • 1
  • 8