.asset file containing ScriptableObject is empty on openingUnity

I’ve been doing a lot of research on this problem, and to quote someone else’s post, I’ve come to two conclusions:

  1. this is a very common problem
  2. it has a solution, but I don’t know what that is yet

I am creating a system wherein a Monobehaviour object stores lists of behaviors that derive from ScriptableObject.

I ran into a problem where I was attempting to drag these gameobjects into my project in order to make prefabs. When bringing them back into the scene, the lists had the correct number of elements, but all of the references had been lost, yielding null reference exceptions on inspection.

So, I attempted to make an .asset file that stored these lists with a reference to each gameobject’s instance ID. So far this works fine, as long as Unity is open. I can store and retrieve data in the .asset file just fine. I can “play” the scene and all is well. But as soon as I quit and restart unity, the asset file comes up empty.

According to what I’ve read, storing ScriptableObjects is the desired route, and creating an asset file that stores a ScriptableObject should work just fine. After encountering a lot of these problems, I tried to implement ISerializable, which took me almost all the way to success, except that apparently .NET cannot serialize ScriptableObjects. Or, at least, that’s the way it seemed based on the SerializationExceptions I got.

Ok, here’s a stripped down version of the code I’m working with:

[Serializable]
public class StorageDataBase : ScriptableObject
{
	public List<int> keys;
	public List<DBEntry> data;

	public void SaveEntry(int id, List<StoredBehaviors> a, List<StoredBehaviors> b)
	{
		if (keys == null)
			keys = new List<int>();
		if (keys.Contains(id))
		{
			DBEntry entry = GetEntry(id);
			if (entry != null)
			{
				entry = new DBEntry(id, a, b);
				return;
			}
		}
		else
			keys.Add(id);

		if (data == null)
			data = new List<DBEntry>();
		data.Add(new DBEntry(id, a, b));
	}

	public DBEntry GetEntry(int id)
	{
		if (data == null)
			data = new List<DBEntry>();

		foreach (DBEntry e in data)
		{
			if (e.id == id)
				return e;
		}
		return null;
	}
}


[Serializable]
public class DBEntry
{
	public int id;
	public List<StoredBehaviors> aList;
	public List<StoredBehaviors> bList;

	public DBEntry(int _id, List<StoredBehaviors> a, List<StoredBehaviors> b)
	{
		id = _id;
		aList = a;
		bList = b;
	}
}

[Serializable]
public class StoredBehaviors : ScriptableObject
{
	public void Begin() { }
	public void DoStuff() { }
}

and here’s how I’m checking the .asset file in my custom inspector:
(code snippet… the editor is rather large)

MonoBehaviourGameObject obj;	
	StorageDataBase db;	

    private void LoadBehaviors()
	{
		db = AssetDatabase.LoadAssetAtPath("Assets/BehaviorAssets/BehaviorDatabase.asset", typeof(StorageDataBase)) as StorageDataBase;
		if (db == null)
			throw new Exception("behavior database not found: either no data was saved, or it was removed");

		obj.a = db.GetEntry(obj.id).a;
		obj.b = db.GetEntry(obj.id).b;
		bool behaviorsLoaded = obj.ValidateBehaviors();	//used elsewhere
	}
	
	
	private void SaveButton()
	{
		if (GUILayout.Button((db == null ? "Save" : "Update") + " Behaviors", GUILayout.ExpandWidth(true)))
		{
			db = AssetDatabase.LoadAssetAtPath("Assets/BehaviorAssets/BehaviorDatabase.asset", typeof(StorageDataBase)) as StorageDataBase;
			if (db == null)
			{
				AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<StorageDataBase>(), "Assets/BehaviorAssets/BehaviorDatabase.asset");
				Debug.Log("creating new db");
			}
			db.SaveEntry(obj.id, obj.a, obj.b);

			AssetDatabase.SaveAssets();
			Debug.Log("saving db entry " + obj.id);
		}
	}

I’ve seen one other post that I haven’t tried yet that talks about just having a public byte array serialized, but before going that route which seems like it might have the same failings, I wanted to ask what’s going on here. I feel as if I’m overlooking something basic.

Thanks in advance

Ok some points that might help you:

  • First you don’t have to load those assets manually. If they are created properly they are linked like any other asset. So if you have a public field of type “StorageDataBase” in a MonoBehaviour, it should be linked to that asset, all the time.
  • It seems you have more than one scriptable object but it looks like you just save one of them. All ScriptableObjects have to be saved manually as an asset. Keep in mind that you can add multiple assets to the same asset file. See AssetDatabase.AddObjectToAsset. An assetfile doesn’t need to represent a single asset. If you add multiple assets to the file all assets will appear with their name as sub-assets (like you see when using fbx modelfiles).

I just created a test with two different scriptable objects. One has a List of the other type. When i create an instance i add it to the list of my “database” scriptable object and as well to the asset file with AddObjectToAsset. That way it physically exists as asset and can be linked as any other asset. When i change the scene or restart unity everything is still fine.