Help please, Saving system giving me a confusing error

Error:

SerializationException: Type 'UnityEngine.Transform' in Assembly 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers (System.RuntimeType type) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.FormatterServices+<>c__DisplayClass9_0.<GetSerializableMembers>b__0 (System.Runtime.Serialization.MemberHolder _) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].GetOrAdd (TKey key, System.Func`2[T,TResult] valueFactory) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.FormatterServices.GetSerializableMembers (System.Type type, System.Runtime.Serialization.StreamingContext context) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo () (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize (System.Type objectType, System.Runtime.Serialization.ISurrogateSelector surrogateSelector, System.Runtime.Serialization.StreamingContext context, System.Runtime.Serialization.Formatters.Binary.SerObjectInfoInit serObjectInfoInit, System.Runtime.Serialization.IFormatterConverter converter, System.Runtime.Serialization.SerializationBinder binder) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize (System.Type objectType, System.Runtime.Serialization.ISurrogateSelector surrogateSelector, System.Runtime.Serialization.StreamingContext context, System.Runtime.Serialization.Formatters.Binary.SerObjectInfoInit serObjectInfoInit, System.Runtime.Serialization.IFormatterConverter converter, System.Runtime.Serialization.SerializationBinder binder) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArray (System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo objectInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo memberNameInfo, System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo memberObjectInfo) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write (System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo objectInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo memberNameInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo typeNameInfo) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize (System.Object graph, System.Runtime.Remoting.Messaging.Header[] inHeaders, System.Runtime.Serialization.Formatters.Binary.__BinaryWriter serWriter, System.Boolean fCheck) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers, System.Boolean fCheck) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph) (at <437ba245d8404784b9fbab9b439ac908>:0)
SaveSystem.SaveScene (SceneData sceneData) (at Assets/Code/SaveSystem.cs:15)
SceneData.SaveScene () (at Assets/Code/SceneData.cs:31)
NextScene+<LoadLevel>d__7.MoveNext () (at Assets/Code/NextScene.cs:44)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <23a7799da2e941b88c6db790c607d655>:0)

SceneData:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneData : MonoBehaviour
{
    public List<Transform> rooms;
    public List<Transform> loot;
    public List<Transform> enemy;

    public void GameData()
    {
        foreach (GameObject r in GameObject.FindGameObjectsWithTag("Rooms"))
        {
            rooms.Add(r.GetComponent<Transform>());
        }

        foreach (GameObject l in GameObject.FindGameObjectsWithTag("Loot"))
        {
            loot.Add(l.GetComponent<Transform>());
        }

        foreach (GameObject e in GameObject.FindGameObjectsWithTag("Enemy"))
        {
            enemy.Add(e.GetComponent<Transform>());
        }
    }

    public void SaveScene()
    {
        SaveSystem.SaveScene(this);
    }

    public void LoadScene()
    {
        GameData();

        GameData data = SaveSystem.LoadScene();

        rooms = data.rooms;
        loot = data.loot;
        enemy = data.enemy;
    }
}

GameData:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class GameData
{
    public List<Transform> rooms;
    public List<Transform> loot;
    public List<Transform> enemy;

    public GameData (SceneData sceneData)
    {
        rooms = sceneData.rooms;

        loot = sceneData.loot;

        enemy = sceneData.enemy;
    }
}

SaveSystem:

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class SaveSystem
{
    public static void SaveScene (SceneData sceneData)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        string path = Path.Combine(Application.persistentDataPath, "scene.Alden");
        FileStream stream = new FileStream(path, FileMode.Create);

        GameData data = new GameData(sceneData);

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

    public static GameData LoadScene()
    {
        string path = Path.Combine(Application.persistentDataPath, "scene.Alden");
        if (File.Exists(path))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(path, FileMode.Open);

            GameData data = formatter.Deserialize(stream) as GameData;
            stream.Close();

            return data;
        }
        else
        {
            UnityEngine.Debug.Log("Save file not found in" + path);
            return null;
        }
    }
}

Ok but I’m curious what exactly the warning means

[System.Serializable] is an attribute used to indicate that it’s safe to serialize a class. (NOTE: This is an older system, more recent serialization schemes may not use this attribute.) You’re trying to serialize a class that isn’t decorated with that attribute, and the serializer is complaining that it’s only allowed to serialize classes with that attribute.

Ok that makes a bit more sense, how would be the best way to fix this, by putting something in front of public List…?

The best way to fix this is to stop trying to save Transforms.

If you were trying to save your own custom class, and there was no reason that it shouldn’t be safe to serialize, then you would add the [System.Serializable] attribute in front of the class definition. (Like you already did with the GameData class.)

In this case, the class that is giving you trouble is Transform. You can’t add attributes to the Transform class without recompiling UnityEngine, because it’s not your class and the source code that defines it isn’t in your project.

Even if you could add the attribute, that might have weird side-effects in other areas that would be hard to predict, because Transform is used in tons of other code that was written on the assumption that Transform is defined in the way that Transform is currently defined, and if you mess with that definition then that code might fail in subtle ways.

And saving the Transform probably isn’t really what you wanted to do in the first place, because I bet lists named things like “loot” and “enemies” contain data that you care about that isn’t actually part of the Transform class.

So what you ought to do is to make a list of what information you actually care about and need to save, and put just that information into a safely-serializable data structure, and serialize that instead.

1 Like

Ok cool so there’s no way to do it with game objects instead of transforms

Wholesale saving of game objects is not directly supported and in many cases, even if you could do it, you would have performance problems. My recommendation is to save only the fields that you really need.

Dang alright, thank you for all the help