rohanharikr
rohanharikr

Reputation: 1811

Only apply changes to Scriptable Object asset on "Save" button click in Unity

[CustomEditor(typeof(Test))]
[CanEditMultipleObjects]
public class TestInspector : Editor
{
  public override void OnInspectorGUI()
  {
    serializedObject.Update();

    EditorGUILayout.PropertyField(serializedObject.FindProperty("Flag1Enabled"));
    EditorGUILayout.PropertyField(serializedObject.FindProperty("Flag2Enabled"));
    
    if(GUILayout.Button("Apply changes"))
    {
      serializedObject.ApplyModifiedProperties();
    }
    if(GUILayout.Button("Discard changes"))
    {
      //Assuming what I think this is doing which is reset all unapplied modifications?
      serializedObject.Update();
    }
  }
}

The issue I am facing with the above code is that the Inspector UI does not get updated when Flag1Enabled or Flag2Enabled toggles are interacted with. The UI only gets updated when serializedObject.ApplyModifiedProperties() is called at the end of the OnInspectorGUI method which messes with my goal i.e. I only want changes to be applied to the S.O. asset on clicking "Save changes" button.

I also thought of creating a copy of the serialized object and making edits to the duplicate object - maybe this is a/the way but looking for simple solutions.

I'm pretty new to Unity - perhaps, I am making this more complicated that it needs to be?

Upvotes: 0

Views: 326

Answers (1)

derHugo
derHugo

Reputation: 90724

//Assuming what I think this is doing which is reset all unapplied modifications?

Yesn't. It is a bit more complicated with editor scripting and serialization. You have to think frame-wise.

Within one call of OnInspectorGUI you have

  • an asset (in your case ScriptableObject instance) on your disk
  • the according current c# class instance (target)
  • the serializedObject which is basically a virtual "in-between" transfer object between the Inspector and the asset
  • And then you have the SerializedPropertys which as part of the serializedObject are also sort of a staging area

Now when you call serializedObject.Update you load the current values from the target (c# instance) values into the staging SerializedProperty.

When you call ApplyModifiedProperties it accordingly applies those back to the actual asset and triggers all sort of other secondary functionalities like dirty-marking (= saving as Unity only writes assets to disk that are marked as dirty), Undo/Redo etc.

You could of course use some "manual" staging system like e.g. this

[CustomEditor(typeof(TestComponent))]
[CanEditMultipleObjects]
public class TestInspector : Editor
{
    bool flag1EnabledValue;
    bool flag2EnabledValue;

    SerializedProperty flag1Enabled;
    SerializedProperty flag2Enabled;

    bool dirty;

    private void OnEnable()
    {
        flag1Enabled = serializedObject.FindProperty("Flag1Enabled");
        flag2Enabled = serializedObject.FindProperty("Flag2Enabled");

        serializedObject.Update();

        flag1EnabledValue = flag1Enabled.boolValue;
        flag2EnabledValue = flag2Enabled.boolValue;
    }

    public override void OnInspectorGUI()
    {
        using (var changeCheck = new EditorGUI.ChangeCheckScope())
        {
            flag1EnabledValue = EditorGUILayout.Toggle(flag1Enabled.displayName, flag1EnabledValue);
            flag2EnabledValue = EditorGUILayout.Toggle(flag2Enabled.displayName, flag2EnabledValue);

            if (changeCheck.changed)
            {
                dirty = true;
            }
        }

        using (new EditorGUI.DisabledScope(!dirty))
        {
            if (GUILayout.Button("Apply changes"))
            {
                flag1Enabled.boolValue = flag1EnabledValue;
                flag2Enabled.boolValue = flag2EnabledValue;
                serializedObject.ApplyModifiedProperties();
                dirty = false;
            }

            if (GUILayout.Button("Discard changes"))
            {
                flag1EnabledValue = flag1Enabled.boolValue;
                flag2EnabledValue = flag2Enabled.boolValue;
                dirty = false;
            }
        }
    }
}

but this might become quite uncanny in more complex types.

Upvotes: 0

Related Questions