5
\$\begingroup\$

I was wondering what the "correct" way to save scriptable objects in Unity that are changed during Play mode. So far I have something that works:

    // changes
    PartyMember Lalala = PartyMemberDictionary.Find("Lalala");
    Lalala.HP += 17;
    print("Lalala's HP: " + Lalala.HP);
    // then saves
    AssetDatabase.Refresh();
    EditorUtility.SetDirty(Lalala);
    AssetDatabase.SaveAssets();

Note: Lalala is a direct reference to a PartyMember scriptable object in my Assets/Resources folder.

I'm still a little shaky on this approach, however. I got this directly from reading this question:

https://forum.unity3d.com/threads/scriptableobject-asset-problem-the-changes-wont-saved-to-disk.229664/

But the commentator did not explain what each of these three lines of code mean.

    AssetDatabase.Refresh();
    EditorUtility.SetDirty(Lalala);
    AssetDatabase.SaveAssets();

Can someone explain these to me? I'm not sure I understand them fully. I assume "dirty" indicates that data changed; please correct me if I'm wrong.

In this link

https://docs.unity3d.com/ScriptReference/AssetDatabase.SaveAssets.html

it asserts that SaveAssets() saves all unchanged asset changes to disk, so why do I need the other Refresh and SetDirty Functions?

In this link

http://answers.unity3d.com/questions/11531/why-doesnt-my-scriptableobject-save-using-a-custom.html

The OP says "as I found out after some trial and error AssetDatabase.SaveAssets doesn't actually save the asset as expected." ... so confused at this point. That leaves me hesitant to continue until I understand what's going on. Perhaps the Refresh and SetDirty cement the job?

Also, in this link

https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html

it mentioned that Undo.RecordObject is preferred. Based on your experiences, is this true? Is EditorUtility.SetDirty(); obsolete in this day and age already? I don't want to use it if I have to change my code in the future.

At the end of the day I simply want something that works.

\$\endgroup\$
3
  • \$\begingroup\$ If the docs say Undo.RecordObject is to be preferred over EditorUtility.SetDirty, why do you need a second opinion on the matter? An explicit statement from the developers of the engine themselves is about as definitive an answer as we can reasonably expect. \$\endgroup\$
    – DMGregory
    Jul 31, 2017 at 3:17
  • \$\begingroup\$ Note that scriptable objects do not serialize during gameplay and using AssetDatabase.SaveAssets wont work. Simply serialize your object to disc on runtime and reload it everytime you play.\ \$\endgroup\$
    – Sidar
    Jul 31, 2017 at 10:47
  • \$\begingroup\$ Are you still looking for an answer to this question? Because I have tested and ScriptableObjects automatically store information during play mode. They don't store information between game sessions. And they don't delete data when loading between scenes. \$\endgroup\$ Feb 24, 2019 at 16:56

1 Answer 1

1
\$\begingroup\$

Actually, it's a known thing they warn you about in the Unity docs (somewhere) that if you drag a prefab or Asset file to a script and then at edit OR runtime you change the contents of that prefab/script without first instantiating it the changes are permanent.

Example: You create a prefab for "Boss" and give him a Health value of 500. If your code looks like this:

public MyEnemyClass BossPrefab;
void OnDamage(int value) {
    BossPrefab.Health -= value;
    if (BossPrefab.Health <= 0)
    { BossPrefab.Health = 0; Die(); }
}

The first time you fight the boss he will have 500 health. If he beats you and has only 23 health left then the next time you start the game and get to the boss he will start with 23 health. So if you ever beat the boss then every single time you get to him he will die immediately.

Basically what you are doing is changing the value that you set on the PREFAB itself and that persists forever. If you want the code to behave as intended you need to first Instantiate the prefab and then make changes to that instance.

With ScriptableObjects, on the other hand, since they are not intended to be instanced but were designed to just store data for you to use in game, you are not supposed to change their values at runtime at all. That is not what they were designed for. That is what prefabs were designed for.

So to answer your question of "How do you save the values set during runtime" the answer is simply: "You don't. As soon as you change the value in the game the values are already permanently saved". If you don't see this because you have the asset visible in the inspector while running, just click on anything else then click on the asset again and notice how the value in the inspector is now no longer what you set it to manually but what it ended up being while playing.

EDIT: For the longest time I didn't understand what the difference was between a ScriptableObject and a prefab since I used both just to store data for use in the game and never actually instantiated it. the answer I got whenever I asked was that prefabs appear in the hierarchy and have a Transform which it simply doesn't need... but none of that is true if you don't actually instantiate the prefab into your scene.

So, again, what is the difference between a ScriptableObject and a run of the mill prefab? Simply this:

  1. You don't instantiate ScriptableObjects at runtime, you just use them directly (Same as you CAN do with prefabs)
  2. By adding a property before the class name you can create a ScriptableObject from the Unity menu (with a prefab you would just select the prefab and hit Ctrl+D to duplicate it)

...and that is IT. So all and all ScriptableObjects are nothing really special and you can just use a prefab to do the same thing only prefabs can be instantiated (or not) and thus offer you more you can do with it.

\$\endgroup\$
1
  • \$\begingroup\$ You have to call EditorUtility.SetDirty for the scriptable object changes to be saved ( otherwise, values will be reset once you close unity \$\endgroup\$
    – Ninja Dev
    Mar 14, 2019 at 12:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .