I am trying to serialize a scriptable object that contains a dictionary which I plan to use to build levels for my game. After the object is created and initialized, I save it as an asset that is then attached to a Monobehavior that generates a level dynamically. Since unity can’t serialize dictionaries I am using some lists in conjunction with the ISerializationCallbackReceiver interface, to save and load the data contained in the dictionary. This is the scriptable object code
public class LevelsDatabase : ScriptableObject, ISerializationCallbackReceiver
{
public Dictionary<RoomBiome, Dictionary<RoomType, LevelInfoListWrapper>> BiomeDictionaries =
new Dictionary<RoomBiome, Dictionary<RoomType, LevelInfoListWrapper>>();
// Serialization variables
[SerializeField]
private List<RoomBiome> _biomeDictionariesKeys = new List<RoomBiome>();
[SerializeField]
private List<RoomType> _levelInfoListKeys = new List<RoomType>();
[SerializeField]
private List<LevelInfoListWrapper> _levelInfoListValues = new List<LevelInfoListWrapper>();
public void GenerateLevelsDatabase()
{
Debug.Log("Generated Database");
LoadLevelInfo();
}
// Database generation code
public void OnBeforeSerialize()
{
try
{
_biomeDictionariesKeys.Clear();
_levelInfoListKeys.Clear();
_levelInfoListValues.Clear();
foreach (var biomeDictionary in BiomeDictionaries)
{
_biomeDictionariesKeys.Add(biomeDictionary.Key);
}
var key = _biomeDictionariesKeys[0];
foreach (var roomTypeDictionary in BiomeDictionaries[key])
{
_levelInfoListKeys.Add(roomTypeDictionary.Key);
}
foreach (var biomeKey in _biomeDictionariesKeys)
{
foreach (var roomTypeKey in _levelInfoListKeys)
{
var biomeDictionary = BiomeDictionaries[biomeKey];
if (!biomeDictionary.ContainsKey(roomTypeKey))
{
continue;
}
var levelInfoList = BiomeDictionaries[biomeKey][roomTypeKey];
_levelInfoListValues.Add(levelInfoList);
}
}
}
catch (Exception e)
{
Debug.Log("Something horrible happened");
}
}
public void OnAfterDeserialize()
{
BiomeDictionaries.Clear();
int index = 0;
foreach (var biomeKey in _biomeDictionariesKeys)
{
BiomeDictionaries.Add(biomeKey, new Dictionary<RoomType, LevelInfoListWrapper>());
foreach (var roomType in _levelInfoListKeys)
{
if (index >= _levelInfoListValues.Count)
{
break;
}
// TODO quick fix, replace it when refactoring
if (roomType != _levelInfoListValues[index].LevelInfoList[0].RoomType)
{
continue;
}
BiomeDictionaries[biomeKey].Add(roomType, _levelInfoListValues[index]);
index ++;
}
}
Debug.Log("Deserialized");
}
RoomBiome and RoomType are enums and LevelInfoListWrapper is a wrapper for a list of LevelInfo objects defined as following:
[Serializable]
public class LevelInfoListWrapper
{
public List<LevelInfo> LevelInfoList = new List<LevelInfo>();
}
LevelInfo is a scriptable object that contains data belonging to the rooms to be used in a Level. It is defined as
[Serializable]
public class LevelInfo : ScriptableObject
{
public RoomType RoomType;
public RoomBiome RoomBiome;
public LevelsEnum Level;
[SerializeField]
private List<EntryPoint> _levelEntryPoints = new List<EntryPoint>();
//TODO add breakable spawners and combat triggers Info
public void AddEntryPoints(IList<EntryPoint> entryPoints)
{
_levelEntryPoints.AddRange(entryPoints);
}
public List<EntryPoint> GetEntryPoints()
{
return _levelEntryPoints;
}
}
LevelInfo has, among its data a list of EntryPoints defined as such:
[Serializable]
public class EntryPoint
{
public string ToLevel;
public RoomBiome ToBiome;
public Vector3 Position;
public int Index;
public EntryPointPosition PositionInRoom;
}
This code works and the serialization/deserialization process is handled correctly when I play and stop the editor. Unfortunately if I quit the editor and restart it, the lists List contained in _levelInfoListValues are not correctly deserialized and contain LevelInfo references initialized with the default constructor. I have read a few posts and references but I am running out of ideas on how to make unity serialize correctly a list of LevelInfoWrapper. The idea to ditch completely unity’s serialization and use somenthing like xml or Json is tempting but before resorting to that I was wondering if anyone has any suggestion about a possible solution.
Thanks!