SerializeHelper - Free save and load utility. (De)Serialize all objects in your scene.

[UPDATE]

Unity Save Load Utility, the successor to SerializeHelper, has been uploaded. An new thread has been created on the Unity forums:

Unity Save Load Utility

[/UPDATE]

SerializeHelper Download Link

So, saving & loading player progress is something that most games need in some form or another, and often, the PlayerPrefs way of doing it is not sufficient. The late and great UnitySerializer is no longer supported and was always something of a monster in terms of complexity, so I made my own system for saving and loading objects in the scene.

So, what is it, and what can it do?

Currently, it is a small collection of simple scripts and classes that allow you to save and load GameObjects by serializeing and writing them to a savegame file via BinaryFormatter. So far, only a GO’s transform, active state, name, and any MonoBehavior scripts on it are saved, along with their variable values, of course. That means that you will have to create your own workarounds for saving and loading things like Renderers and other components. I did not include them because their importance varies from project from project.

How do you use it?
Simple: Open the provided sample scene, and either hit Escape or F5 and F9 on your keyboard for the menu or QuickLoad and QuickSave, respectively. Note that a save game file and possible directory structure will be created on your hard disk. Take a look at the “SaveLoadMenu” script to see where files are saved.

How it works, in simple words:

At the core lies the SaveGame class. an instance of this class is created when we want to save. This class holds a list of instances of the class SceneObject.
SceneObject in turn represents a GameObject in a form that can be serialized. It holds a GO’s name, id, parent’s id (if any), Transform values (rot/pos/scale), active state, and a list of instances of the class ObjectComponent.
ObjectComponent represents one MonoBehavior (currently) component (“script”). It holds the component’s name as a string, and a Dictionary<string,object> “fields”. This dictionary holds a field’s name as key and a field’s value as value, stored as an object.

Each GameObject that should be able to be saved and loaded needs two things:

  1. A component “ObjectIdentifier”
  2. A Prefab of it somewhere in the Resources folder, which of course also needs an ObjectIdentifier component.

ObjectIdentifier is a MonoBehavior component that holds a GameObject’s prefab name and id and parent’s id values.

If we want to save the GameObject in our scene, the following happens:
(0.) At runtime start, the prefabDictionary is filled. This collection holds references to all GameObjects in the Resources folder which have an ObjectIdentifier component. This dictionary is later used to reconstruct a GameObject.

  1. A new instance of SaveGame is created
  2. All occurences of the ObjectIdentifier is collected, thereby creating a collection of GameObject that should be saved.
  3. For each GO, a new instance of SceneObject is created. This gets “filled” by “packing” the GameObject’s components into serializable form, and copying the ids, Transform, and other misc. data.
  4. Each SceneObject instance created for a GO is added to the SaveGame’s list of SceneObjects.
  5. The SaveGame is serialized by the static function SaveLoad/Save method and written to a file in the hard disk. ISerializationSurrogates are used to serialize Unity-specific types like Vector3 (a few examples are provided)

If we want to load a saved game, the following happens:

  1. All Objects which are not tagged as “DontDestroy” (persistent objects, like managers etc.) are destroyed, thereby clearing the scene and creating a blank slate.
  2. The SaveGame instance is deserialized from file
  3. Each SceneObject in the SaveGame instance’s list of SceneObjects is “unpacked”; A SceneObject’s prefabName value is used to pull a prefab from a the prefabDictionary and instantiate a copy of it.
  4. The newly instantiated GO’s components and misc. values are reconstructed from the data stored in the SceneObject instance.

I want to stress the fact that while it has been tested, it represents a bare-bones aproach and should be considered a stepping stone on the way to implement your own save/load feature in your project. This package has been kept simple on purpose, both because it should be easy to understand and also because each project is different in it’s needs.

It’s also worth noting that serialization is, at least by me, considered an advanced topic which requires copious amounts of research and trial and error in order to understand it. I recommend the Microsoft Developer Network (MSDN) and StackOverflow.com for further information.

Before using the assets, I strongly recommend at least cursory reading of all the scripts. I included numerous comments and some readme files. You absolutely HAVE to get a basic understanding of what happens :slight_smile:

SerializeHelper Download Link
(Alternate download from my own webspace: SerializeHelper Download Link)
(Project folder from my own webspace, in case the asset package doesn’t work: SerializeHelper Project Folder)

2187332–145065–SerializeHelper.unitypackage (32.3 KB)

7 Likes

Comments & Critique are welcome. I am no pro programmer by any means, I just wanted to share what my own efforts in the field of serialization have brought me: A solid framework for saving and loading player progress.

Things that would make reasonable additions:
Solutions for serializing other Unity-spcific types like Mesh, Renderer, Material, …
Handling GameObject and Transform references directly on the field operating level
Saving and loading single GameObjects for selective saving / loading (it’s pretty much already there with Pack/Unpack GameObject, but not implemented)

hey thanks ill try this… :slight_smile:
ive been looking for systems that can serialize “scenes” (most of then are paid or not supported any more)
i hope this could be my answer thanks any way God bless :slight_smile:

Considering that

Considering that a scene is nothing more than the GameObjects in it as well as some misc information like skybox etc., then it’s just a matter of saving the GameObjects and the misc data. I originally created this asset to save maps made for a voxel-type game, but of course it is equally suited for normal save games.

So I’m looking into this solution my self and was hoping to find here what I was looking for.
Unfortunatly I found your Texture2D surrogate to look a lot like mine at the moment, empty :).

I see you do have a color surrogate, so my question is if this is the best way to go: serialize the pixels, via a Get/SetPixels32?

Well as explained in the project, this particular ISS is just there to prevent non-serializable classes from throwing an error if they are a member of a custom class that is to be serialized. To actually serialize a Texture2D, yeah, Get/SetPixels looks like the way to go. I would only do this if the texture is actually created or changed at runtime, otherwise it’s far easier to just save the name of the texture as a string and use that with Resources.Load to assign it to the Texture2D variable again after loading.

Yes, thats my issue. I’m loading a massive amount of textures at runtime and I want to store them as one item to ease disk IO when I load them again later. Also I want to compress them after loading and do this only once.

After some testing I found it is not the way to go. As I feared before (and is actually quite obvious afterwards) it is very inefficient on both the performance side and the storage side.

Taking a 720P texture, and serializing it will take up at least (1280*720)*4 bytes of space.
But Unity has to generate those pixels first, and this takes up a lot of time.

5 textures generated a file of almost 60 megabytes and it took a long time doing it (did not time it but almost a minute I think).

So I kept looking an came accros GetRawTextureData for the millionth time in code completion, but this time I also noticed the UpdateRawTextureData. I had not noticed it because I kept looking at the Set methods and thought you could only get the raw data.

Well this is exactly what you want. I got a 3Mb file for 5 and a 900 Mb file out of 1500 720P textures, and it takes less then 10 seconds to store on my Surface Pro 3, and about the same amount of time to load.

To improve this I’m going to see if compressing the stream before writing would be a good space/performance trade off.

Good to see you are making progress!

Uncompressed images take up quite a lot of space, I suggest looking into either PNG compression or LZMA (which is generic, not specific to images, so you can reuse it for other stuff - but it’s still very good compression for images, it might even be better than PNG. I believe LZMA is used by Unity for quite a few things.). Then again, this depends on your target device(s), PNG is probably faster to compress&load than LZMA on lower-end devices. Oh, and don’t use Unity’s built in EncodeToPNG - it will only run on the Unity main thread. If you target devices has more than 1 core, you will get much better performance if compress and decompress multithreaded.

Since you are using RawTextureData byte arrays, I would suggest trying with LZMA first, these
might help:
http://www.nullskull.com/a/768/7zip-lzma-inmemory-compression-with-c.aspx
https://github.com/episage/LZMA-compression

I was thinking about something like that, PNG is no good, as I want to serialize the whole lot in to one steam and then compress the stream before it is written.

The LZMA example looks like what I was thinking and I’m going to try it out!

After a lot of experimentation today, I managed to reconstruct GameObject references in scripts without the user having to add seperate variables in his scripts that hold the relevant ids. This has been a major obstacle since the very start and I’m really happy that I finally tackled it. The same procedure can be used for similar references, of course (Transform, for example). I will do some more testing and upload the modified version of SerializeHelper.

Thank you sooooo much Cherno - my life savior !!! It works like a charm !!! :))))

You did a really good job Cherno, although I do have a question. Can this save an object with it’s children, if yes, how? The children have things like rigidbody’s, colliders and springs attached to them too.

Thanks to both of you.

@ Lucky: Yes, it can save and load an object with it’s complete hierarchy intact. Let me explain.

The most basic case is an object where the hierarchy doesn’t change during play; No children are added or existing ones deleted or parented to other objects. In this case, nothing else needs to be done since when loading a saved game, the a clone of the prefab of the object just gets instantiated.

If the hierarchy does change during play, then all children which may change need to have ObjectIdentifier components attached, and of course they also need Prefabs. Everything else is done by the script: It checks all children of an object for OIs and if one is present, saves that object’s parent ID so it can be re-parented after loading.

@Cherno Thanks for the quick answer. I don’t add, delete or parent existing objects as children runtime, so I’m fine just having an ObjectIdentifier on the parent. But I do have another question, how can I trigger a save or load function with another script? I have an object with a trigger, so if a player is in the trigger, he can press the button and the game will save. I already have everything setup, but I just need to be able to trigger a save or load function, like SaveLoadScript.Save();

@Cherno Okay so I found out, that SaveLoad.Save(); is working, but, how do I define which game to load? Load() takes a string, but I don’t get what SaveGame saveGame means.

@Cherno I think I figured it out! I just had to make a public SaveGame BouncersSave, then in the editor, I could choose the name of the Savegame.

@Cherno I really need help on this one, I just can’t get my game to save. What I have is a script that calls SaveLoad.Save(); This doesn’t give any errors, but it won’t save either. How would I save and load the game? I am looking through the SaveLoadMenu script, but I just can’t really figure this one out :smile:

You need to call the SaveGame function from the SaveLoadMenu script, since only that function will actually create a new instance of SaveGame and fill it with packed GameObjects (as SceneObjects).
Take a look at the SaveGame() function in SaveLoadMenu: It takes no parameter so it’s assumed that it should use the QuickSave slot, which it does by calling the SaveGame function which does take a paramter (string saveGameName), and uses “QuickSave” in this case.
If you want to save and load different save games, then you just have to pass the right name as a string to the SaveGame and LoadGame functions. You could do this by keeping a public string variable in the SaveLoadMenu script which holds the SaveGame name, which is then used with the corresponding functions.

Example:

(insert the public string nameOfSaveGame; line and repalce the OnGUI function)

public class SaveLoadMenu : MonoBehaviour {

    public bool showMenu;
    public bool usePersistentDataPath = true;
    public string savePath;
    public Dictionary<string,GameObject> prefabDictionary;
        //new line:
        public string nameOfSaveGame;
//...

void OnGUI() {
     
        if(showMenu == true) {
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.BeginVertical();
            GUILayout.FlexibleSpace();
         
            if(GUILayout.Button("Exit to Windows")) {
                Application.Quit();
                return;
            }

            nameOfSaveGame = GUILayout.TextField(nameOfSaveGame, 25);
         
          if(GUILayout.Button("QuickSave")) {
                SaveGame();
                return;
            }
         
            if(GUILayout.Button("QuickLoad")) {
                LoadGame();
                return;
            }
        
            if(GUILayout.Button("Save Game: " + nameOfSaveGame)) {
                SaveGame(nameOfSaveGame);
                return;
            }
         
            if(GUILayout.Button("Load Game: " + nameOfSaveGame")) {
                LoadGame(nameOfSaveGame);
                return;
            }

            GUILayout.FlexibleSpace();
            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
        }
    }
//...

Normally, when opening the menu, you would check the save files on your hard disk and display them as a list of buttons so the user can just click on one and the game gets loaded / saved. The method above just provides a quick solution for testing purposes.