I'm making an effort to use ScriptableObjects more in my game architecture, after watching a fantastic Unity Unite talk on the subject. The central idea is to use ScriptableObjects, during runtime, as a kind of pluggable, mutable global data container. It's intended to replace things like the singleton pattern in a more testable, less tightly-coupled way.
I'm not going to go into detail on the arguments in favor of this, but I do definitely recommend watching the talk.
When using this pattern, you'll probably run into data-container-style ScriptableObjects that you want to persist between play sessions. The problem I'm running into is that, when Unity's serializer is run on a ScriptableObject that references other ScriptableObjects, and those referenced ScriptableObjects have changed during runtime, the serializer ignores the changes on those sub-SOs.
Is there a good way to serialize nested SOs containing data that changes during runtime?
For some examples/more in-depth background:
In the game I'm working on, I'm emulating a file system that multiple otherwise-independent game objects are interested in. It looks somewhat like this:
public abstract class FilesystemObject : ScriptableObject
{
public string FileName;
}
[CreateAssetMenu(fileName = "NewDirectory.asset", menuName = "Directory")]
public class FilesystemDirectory : FilesystemObject
{
public List<FilesystemObject> Contents;
}
[CreateAssetMenu(fileName = "NewTextFile.asset", menuName = "Directory")]
public class FilesystemTextFile : FilesystemObject
{
public string Contents;
}
In my project directory, I have a FilesystemDirectory
asset file named FSRoot.asset
that represents the root directory of the filesystem. It's populated with all the files that need to exist when the player first launches the game.
If a MonoBehaviour wants to read or write to this filesystem (say, an in-game text editor), all I have to do is expose a FilesystemDirectory
field to Unity's serializer, and then hook FSRoot
up to that field using the inspector. Then, during runtime, if the player creates, edits, or deletes any filesystem data, those manipulations are done directly on the FSRoot
object. Any other MonoBehaviour with the same reference to FSRoot
sees the same mutated data, since those changes persist in memory.
However, unless you're in the editor, those changes do not persist between sessions. You have to serialize and deserialize the data manually for that. However, when I serialize FSRoot
using Unity's JsonUtility, it only captures changes that have happened to FSRoot
directly, and not any changes in subdirectories/files inside FSRoot
. Any files that were authored in edit time are completely unchanged.