Help with create child asset undo

Hello,

in a custom inspector I’m adding new items to an array when a button is pressed:

if (GUILayout.Button("Add"))
{
    // T derives from ScriptableObject
    var newItem = CreateInstance<T>();
    newItem.hideFlags = HideFlags.HideInHierarchy;

    AssetDatabase.AddObjectToAsset(newItem, target);
    AssetDatabase.SaveAssets();

    // itemsProperty is of type T[] (an array of T)
    ++itemsProperty.arraySize;
    itemsProperty.GetArrayElementAtIndex(itemsProperty.arraySize - 1).objectReferenceValue = newItem;
}

It works fine but I can’t get the undo system to function properly. I tried various solutions involving the Undo class but I had no luck so far.

Any help is much appreciated.

Thank you very much.

Edit:
To make it clear, I’d like to have a single undo operation to:
1 - Revert add object to asset
2 - Revert array size increment

Edit 2:
To be even more clear, the problem is that the array size change is undone but the child asset remains (as far as I can tell…)

You can do this by using a combination of SerializedObject/Property (which you’re already using I think) along with the [Undo.RegisterCreatedObjectUndo](https://docs.unity3d.com/ScriptReference/Undo.RegisterCreatedObjectUndo.html) method.

// T derives from ScriptableObject
var newItem = CreateInstance<T>();
//newItem.hideFlags = HideFlags.HideInHierarchy;
newItem.hideFlags = HideFlags.None; // Using None so we can see the object being removed on Undo. Use HideInHierarchy when you're done testing.

// Register the created object with the undo system.
Undo.RegisterCreatedObjectUndo(newItem, "<Your Undo text here>")
AssetDatabase.AddObjectToAsset(newItem, target);

// Update the serialized property array.
serializedProperty.arraySize = serializedProperty.arraySize + 1;
var arrayElement = serializedProperty.GetArrayElementAtIndex(serializedProperty.arraySize - 1);
nodeArrayElement.objectReferenceValue = newItem;

// Apply the changes. Automatically adds to the current undo group, causing the array change to be grouped with the object creation change.
serializedObject.ApplyModifiedProperties();

Don’t forget, you’ll need to save the project to see the Scriptable Object update in the project window. Alternatively, you can call AssetDatabase.SaveAssets() from your script if you want to force a save.

1 Like

@Pelican_7 Thanks! This approach works for me, but there is some weirdness going on with the project window during undo/redo. I have a Graph ScriptableObject that has a list of Nodes. When I add a Node to the graph, the Graph is properly updated as well as the project view.

However, if I undo or redo a change, the project view doesn’t update until I save the whole project. (Though it seems that the Graph is properly updated). I’m not exactly sure why this is happening:

[CreateAssetMenu(menuName = "SO/Overworld/Graph")]
public class OverworldGraph : ScriptableObject
{
    [SerializeField]
    private List<OverworldNode> overworldNodes;


#if UNITY_EDITOR
    public void AddNode()
    {
        Undo.RecordObject(this, "added node to graph");

        var node = CreateInstance<OverworldNode>();
        node.hideFlags = HideFlags.None;

        AssetDatabase.AddObjectToAsset(node, this);

        overworldNodes.Add(node);

        Undo.RegisterCreatedObjectUndo(node, "Node added");

        AssetDatabase.SaveAssets();
    }
#endif
}