Hello everyone,
I found some behavior that might be a bug or me misunderstanding something.
We have some legacy code that uses a lot of scriptable objects. One of them looks like this:
[CreateAssetMenu(fileName = "ShipPreset", menuName = "Scriptable Objects/ShipPreset")]
public class ShipPreset : ScriptableObject {
public List<ModulePosition> baseModules;
}
where ModulePosition looks like this:
[Serializable]
public class ModulePosition {
public float x;
public float z;
public int moduleTypeIndex;
[JsonIgnore, NonSerialized]
public ushort cost = 1; // cannot be 0
[JsonIgnore, NonSerialized]
public ushort bestCost = ushort.MaxValue;
public void ResetCost() {...
}
}
We created an instance of the ScriptableObject in the editor, added some entries to its list and assigned it. It was then used and modified in-game (we have since changed this but that’s how I found the issue), resulting in the list being empty. When the game was stopped, the instance still had an empty list. I noticed however that the yaml (.asset) file Unity created for the instance was unchanged and indeed, when I restarted Unity, the list was filled again.
Domain reload, reimporting the asset and recompiling had no effects.
a) Why is the list still empty after play mode ended?
b) Why is a) not reflected in the file?
c) Why does my data re-appear after I restarted Unity?
I would expect either the list to be empty even after a Unity restart or play mode changes discarded all together.
Thanks for any insight.
A: Because any changes to assets (not just scriptable objects) done via code will persist between play mode sessions, however…
B: Because the file hasn’t been dirtied and thus Unity hasn’t written the changed data to disk. The copy you edit/view in the project window/inspector is the instance in memory, not on disk.
C: See above.
1 Like
Thanks for the hint to dirtying. I looked it up and it just says dirtying ensures “the change is registered and not lost”. Follow-up questions:
a) Does “registered” mean: “saved to file”? Or does it rather mean something like “will be saved to file”?
b) I also found some people mentioning “save project” - is that what saves dirty assets?
c) And, as SetDirty() is an editor utility, does that tell me I’m not supposed to modify ScriptableObjects / assets at runtime?
d) Is there another way to reset ScriptableObjects (or any asset that has been modified at play time) without restarting Unity?
Bottom line is whenever a value is changed in an asset, Unity needs to be informed that there has been a change so it knows to write the values to disk next time the user saves.
Normally this is done automatically with default inspectors, and correctly written custom inspectors (namely through SerializedProperty’s). When an asset is modified via code such as in editor tooling, Unity needs to be informed said asset has changed, which is where EditorUtility.SetDirty() comes in.
This is true of all assets. Just scriptable objects get used as continuously mutable assets in a lot of cases.
Nothing is saying you can’t, but it helps to understand what will happen when you do.
You can write editor tooling to reset certain assets when exiting play mode.
1 Like
Thanks a lot, I now understand much better what happened.
I have one last question that is related. We had a couple cases where materials were modified at runtime and sometimes, because of floting point inaccuracies, the materials would be different after a play mode session. We noticed because their files would show up on git as changed. This tells me that in that case, Unity knew the material asset was changed and also saved it. How does that relate to what we just talked about?
Iunno. Maybe someone/thing has inadvertenly dirtied said materials. Maybe Unity has special handling of materials as they work differently to other assets. I’m only guessing though.
1 Like