Unity types unable to be serialized

Hello,

I’ve written a custom inspector for GameObjects that can serialize/restore data changed at runtime (I can save the values I’ve changed at runtime, so they don’t revert after playmode is stopped). However, most Unity types can’t be serialized using the code from System.Runtime.Serialization. For example, I’d like to serialize Vector3 etc. (seems simple enough.)

Does anyone know of a work-around? Right now I can only operate on basic types; bool,string,float,int etc.

Surrogates are the right idea, yes. Elaborating more on Zeanog answer: There are two things here. Do you want to actually convert the Unity objects to serializable data that you could move around (say network/file stream)? - or do you only care about data persistence between assembly reloads? - If it’s the latter, see [this hack here][1]. But I assume you’re asking about the first case, again, you need some sort of converters/surrogates. Here’s what I did in the [saving system][2] of [VFW][3]:

  • I attach a SaveMarker to whatever gameObjects I want to save, and then choose what components I want saved
  • When saving, I iterate over all the gameObjects in the scene and get those marked with SaveMarker
  • Foreach object, I save it and whatever components marked for saving.
  • When saving an object, I lookup a converter for it. The convert specifies how the object is to be saved.

An example converter, is the GameObjectConverter:

public class GameObjectConverter : SaveConverter<GameObject>
{
	public override void SerializeInstanceIntoData(GameObject instance, Dictionary<string, fsData> data)
	{
		data["name"]     = Serialize<string> (instance.name);
		data["tag"]      = Serialize<string> (instance.tag);
		data["layer"]    = Serialize<int>    (instance.layer);
		data["isStatic"] = Serialize<bool>   (instance.isStatic);
		data["id"]       = Serialize<string> (instance.GetId());
		data["parentId"] = Serialize<string> (instance.GetParentId());
	}

	public override void DeserializeDataIntoInstance(GameObject instance, Dictionary<string, fsData> data)
	{
		instance.name     = Deserialize<string> (data["name"]);
		instance.tag      = Deserialize<string> (data["tag"]);
		instance.layer    = Deserialize<int>    (data["layer"]);
		instance.isStatic = Deserialize<bool>   (data["isStatic"]);
	}
}

For Transform for ex, you save it’s localPosition, localEulerAngles and localScale. For MonoBehaviours it’s a bit more tricky. You need to reflect the behaviour and get whatever serializable fields it had, and serialize them individually:

public class MonoBehaviourConverter : SaveConverter<MonoBehaviour>
{
	public override void SerializeInstanceIntoData(MonoBehaviour instance, Dictionary<string, fsData> data)
	{
		var members = SerializationLogic.Default.GetMemoizedSerializableMembers(instance.GetType());
		for(int i = 0; i < members.Count; i++)
		{
			var member = members*;*
  •  	member.Target = instance;*
    
  •  	data[member.Name] = Serialize(member.Value);*
    
  •  }*
    
  • }*

  • public override void DeserializeDataIntoInstance(MonoBehaviour instance, Dictionary<string, fsData> data)*

  • {*

  •  var members = SerializationLogic.Default.GetMemoizedSerializableMembers(instance.GetType());*
    
  •  for(int i = 0; i < members.Count; i++)*
    
  •  {*
    

_ var member = members*;_
_
member.Target = instance;_
_
member.Value = Deserialize(data[member.Name]);_
_
}_
_
}_
_
}_
_
(Not going into detail, there’s more things to cover like what you do when loading if a component went missing? or a gameObject was destroyed etc)_
Now, how do you persist references to Unity objects like Texture, AudioClip, etc? - Well, the trick here is that we care about saving/loading our data, regardless how. One approach is to get the byte information of textures/audio and serialize that, but that’s not necessary to persist the information. The thing is, those textures/audios are already there in your project so you just need to reference them, no need to recreate them. So what I do is I have a Store object, that has a serializable dictionary to save assets. When the serializer come across a Texture, AudioClip etc, it just stores it in the Store:
public class Store : BetterScriptableObject
_
{_
_
public Dictionary<string, UnityObject> Items*_
* {*
* get { return _items ?? (items = new Dictionary<string, UnityObject>()); }
_
}*

* [Serialize, Readonly, FormatMember(“$name”), FormatPair(“$valueType: $key”)]*
* private Dictionary<string, UnityObject> _items;*

* ///

*
* /// A dictionary of keys representing all the item types we could store*
* /// and values being whether or not the type needs inheritance support from the serializer*
* ///
*
* public static readonly Dictionary<Type, bool> ItemTypes = new Dictionary<Type, bool>()*
* {*
* { typeof(Mesh), false },*
* { typeof(AudioClip), false },*
* { typeof(Material), false },*
* { typeof(PhysicMaterial), false },*
* { typeof(Flare), false },*
* { typeof(GUIStyle), false },*
* { typeof(Texture), true },*
* { typeof(RuntimeAnimatorController), true },*
* { typeof(AnimationClip), true },*
* };*

* public static Store Current*
* {*
* get { return StoreManager.Current.Store; }*
* }*

* public string StoreAsset(UnityObject asset)*
* {*
* var name = asset.name;*
* Items[name] = asset;*
* return name;*
* }*

* public string StoreComponent(Component comp)*
* {*
* var key = comp.gameObject.name + comp.GetType().Name;*
* Items[key] = comp;*
* return key;*
* }*

* public UnityObject RetrieveItem(string key)*
* {*
* UnityObject result;*
* if (!Items.TryGetValue(key, out result))*
* {*
* Debug.LogError("Trying to retrieve an item that doesn’t exist: " + key);*
* }*
* return result;*
* }*
}
///


/// The FullSerializer converter to use to serialize/deserialize asset references (Textures, Meshes, Materials, AudioClips, AnimationClips, etc)
/// Instead of serializing the actual bytes of meshes, audio etc we just save thsoe references into a store object live in the scene.
/// When loading, we just ask the store to give us the item.
/// Note the use of the name of the assets meaning assets you want to save must have unique names
///

public class fsAssetReferenceConverter : fsConverter
{
* public override bool RequestCycleSupport(Type storageType)*
* {*
* return false;*
* }*

* public override bool RequestInheritanceSupport(Type storageType)*
* {*
* return Store.ItemTypes[storageType];*
* }*

* public override bool CanProcess(Type type)*
* {*
* return canProcess(type);*
* }*

* private static Func<Type, bool> canProcess;
_
private static Func<Type, bool> canProcess*

* {*
* get*
* {*
* return _canProcess ?? (canProcess = new Func<Type, bool>(x =>
_
{*

* if (Store.ItemTypes.ContainsKey(x))*
* return true;*
* return Store.ItemTypes.Keys.Any(x.IsA);*
* }).Memoize());*
* }*
* }*

* public override fsStatus TrySerialize(object instance, out fsData serialized, Type storageType)*
* {*
* // StoreItem gives us back the name of the asset*
* serialized = Store.Current.StoreAsset(instance as UnityObject);*
* return fsStatus.Success;*
* }*

* public override fsStatus TryDeserialize(fsData data, ref object instance, Type storageType)*
* {*
* // the data holds the name of the asset since that’s what we serialized…*
* instance = Store.Current.RetrieveItem(data.AsString);*
* return fsStatus.Success;*
* }*
}
The output of the serialization is pure text (JSON) (see the end of the video linked at the beginning) And that’s pretty much it. For more detailed information, check out VFW linked above as well.
_[1]: (SOLUTION) How to serialize interfaces, generics, auto-properties and abstract System.Object classes? - Questions & Answers - Unity Discussions
[2]: VFW SaveSystem | Quick Demo - YouTube*

_*[3]: http://forum.unity3d.com/threads/free-vfw-full-set-of-drawers-savesystem-serialize-interfaces-generics-auto-props-delegates.266165/*_

We use ISerializationSurrogate to serialize Unity objects and scripts.