How to Serialize and Deserialize a Dictionary Containing Gameobject and a Vector2 in Unity

Viewed 4097

I am making a Level Editor for a game that can edit levels in unity's edit mode. It saves progress in the end which can be loaded back when the game is running, but turns out you cannot serialize Unity's Gameobject or any custom C# object by unity (Even if it says so). I was able to serialize most of the stuff but stuck with one Dictionary object.

Now, here is the problem.

[HideInInspector] public Dictionary<GameObject, Vector2> occupants;

This Contains a Dictionary of a GameObject and a Vector2 (Vector 2 is the position in 2d grid and not 2d space). None of it is serializable by the normal method.

 public static void SaveDataValues(DataValuesToSave dataValues, string level)
 {
     BinaryFormatter formatter = new BinaryFormatter();
     string path = Application.persistentDataPath + level + ".dat";
     FileStream stream = new FileStream(path, FileMode.Create);

     DataValuesToSave data = dataValues;

     formatter.Serialize(stream, data);
     stream.Close();
 }

How can I serialize this Dictionary? Any suggestions would be appreciated.

1 Answers

Just in general: You shouldn't use BinaryFormatter anymore at all!


You can build a wrapper class implementing the ISerializationCallbackReceiver interface. In their example there is even almost your exact use case!

A slightly modified solution could look like e.g.

[Serializable]
public class YourDictionary: Dictionary<GameObject, Vector2>, ISerializationCallbackReceiver
{
    [HideInInspector][SerializeField] private List<GameObject> _keys = new List<GameObject>();
    [HideInInspector][SerializeField] private List<Vector2> _values = new List<Vector2>();

    public void OnBeforeSerialize()
    {
        _keys.Clear();
        _values.Clear();
    
        foreach (var kvp in this)
        {
            _keys.Add(kvp.Key);
            _values.Add(kvp.Value);
        }
    }
    
    public void OnAfterDeserialize()
    {
        Clear();
    
        for (var i = 0; i != Math.Min(_keys.Count, _values.Count); i++)
        {
            Add(_keys[i], _values[i]);
        }
    }
}

this way you keep the entire functionality of the dictionary and its interfaces but simply add the serialization on top.

and then in your other scripts you use

[HideInInspector] public YourDictionary occupants;

and use it just like a Dictionary<GameObject, Vector2> (I mean Add, Remove, Clear, foreach etc).


And here it is in action. I just used this simple test script

public class NewBehaviourScript : MonoBehaviour
{
    public GameObject obj;
    
    [HideInInspector] public YourDictionary occupants;

    [ContextMenu(nameof(Add))]
    private void Add()
    {
        Vector2 vec = obj.transform.position;
        occupants.Add(obj, vec);
    }

    [ContextMenu(nameof(Apply))]
    private void Apply()
    {
        foreach (var kvp in occupants)
        {
            kvp.Key.transform.position = kvp.Value;
        }
    }

    [ContextMenu(nameof(Remove))]
    private void Remove()
    {
        occupants.Remove(obj);
    }
}

and can now happily store, remove and apply positions for objects in the scene ;)

  • I first store 4 objects' positions.
  • Then I move them somewhere else and save the scene
  • I unload the scene and create a new empty scene
  • I load back the original scene
  • Result after hitting Apply all objects are back in position, which means that the dictionary was successfully (de)serialized ;)

enter image description here

Related