Does Undo.RecordObject support marking ScriptableObjects dirty?

As far as I can tell it does not. Here’s some editor code for removing weapons from a set:

public class WeaponSet : ScriptableObject
public class Weapon : ScriptableObject

WeaponSet[] unreleased_sets = Resources.LoadAll<WeaponSet>(unreleased_weapons_path);
foreach (WeaponSet weapon_set in unreleased_sets)
{
    Undo.RecordObjects(weapon_set.m_WeaponsInSet, "Unlink unreleased weapon set");
    foreach (Weapon weapon in weapon_set.m_WeaponsInSet)
    {
        weapon.m_AssociatedSet = null;
        // Saving does not write changes to disk without SetDirty.
        //~ EditorUtility.SetDirty(weapon);
    }
}
AssetDatabase.SaveAssets();

I must use SetDirty for Unity to write these changes to disk. (Otherwise the changes are visible in editor, but lost when Unity is closed.)

SetDirty documentation says:

the only remaining situation in which you should use SetDirty is if you are modifying non-scene objects via some other means, and specifically do not want create an undo entry for your modification.

I want an undo entry (and RecordObject correctly makes an undo entry), so it sounds like I shouldn’t use SetDirty. However, I don’t understand how to get Undo to mark the ScriptableObject as dirty for me.

Sorry for waking up an old thread, but I think you have to do both when modifying a ScriptableObject via an Editor script:

    Undo.RecordObject(myThing, "Do my thing");
    myThing.value = 42;
    EditorUtilities.SetDirty(myThing);

In my experiments, I am only able to get the change to a ScriptableObject made via an Editor script to save to disk if I actually perform an Undo operation prior to the manual Save operation. Until then, the data just doesn’t get written out.

Modifying the SO via the Inspector instead of an Editor script works as I’d expect - no need to Undo if changes are made there, and they appear on disk immediately following a Save.

Since the question got bumped already:

I’m pretty sure one of your issues is that you should use

Undo.RecordObjects(weapon_set, "Unlink unreleased weapon set");

instead of

Undo.RecordObjects(weapon_set.m_WeaponsInSet, "Unlink unreleased weapon set");

We don’t know what “m_WeaponsInSet” is but it seems like it’s a collection of some sort. So probably a List or Array. Though in that case your line of code would not compile at all since RecordObject can only be used on objects derived from UnityEngine.Object. Since your Weapon class is a ScriptableObject on its own, since you modify the Weapon instance you have to call RecordObject on that instance. Nested ScriptableObjects won’t propergate the RecordObject down to other referenced assets. Each ScriptableObject is an asset on its own. So calling RecordObject on “weapon_set” would most likely don’t do anything in your case since you do not modify any data in your weapon_set object. You do modify your individual Weapon instances which you do not register for Undo operation.

If you want to group all those individual Weapon changes into a single undo action, you should look into undo groups. You still need to register each weapon object but you can combine all those changes into a single undo group.