Save and load object with his child

Hi everybody !

I would like to know what is the best way to save a object with his children :slight_smile:

I have a object with multiple children’s object which contain each one different Boolean, variable, text ect…

And I want to know how to save this group of object and load them easily when my game starts:)

Thanks you for youre help !
And have a good day !

Hi.

What are you looking for? Do you want a Serialization library or a complete Save System?

I don’t want to promote my asset, but i am currently developing an update for Save Game Pro that lets you save your game object with children and all savable components.

Here is the development board: Trello

The update will be released in this month.

Thanks.

Yes that it! But i’m looking for a android system and I don’t want to paid , I prefer know how to do and doing my own system, even if it’s a lot of work !

But thanks for youre answer , if you have some advice for me I’ll really appreciate! :)!

Have a good day !

Yes, I can teach you how to save a game object, because I was implemented it in my asset.
You need a serialization method, I prefer Binary because it is safe and fast.

Now you can use BinaryWriter and BinaryReader to read/write the Game Object to file and read it back.
Also, you can use MemoryStream in unsupported platforms and save the data to PlayerPrefs.

Do you need a full solution?

(Excuse me, but I can’t provide the full solution now because here is night and I am going to sleep, I will provide it tomorrow if you ask it)

Thanks for youre answer!

Okay ! So I need to learn about serialization!
I’m working on android :slight_smile: do you think that binary system is more appropriate to my project? This is not for very “secure” things , no at this point of my project.
I know how about playerprefs but I think that is not possible to save Boolean and component :confused:

Can you learn to me how to save object and his variable and load it ?

I’ll really appreciate! ( sorry for my English sometimes , I’m French ^^')

What is the full solution? I want to understand how it’s work because I don’t want to judge copy and paste some script ^^’

Have a good night ! And thanks again for youre help !

Hi again.

I am so happy to hear you want to learn :wink:
Also, you can use PlayerPrefs to store Integer, Boolean, String.

I will teach you to develop a Save System like Save Game Pro and own it, also, i have planned to publish this tutorial on my blog.

And also, Binary is secure and fast and it works almost everywhere.

Let’s dive in and Develop a Save System.

Getting Started
Create a new Unity Project and call it Save System then Open it.
Create a new folder called SaveSystem and create these C# scripts in this folder:

  • SaveGame
  • SaveGameSettings
  • SaveGameWriter
  • SaveGameReader
  • SaveGameTypeManager
  • SaveGameType
  • SaveGameStorage
  • SaveGameFileStorage
  • SaveGamePlayerPrefsStorage

Each file has it’s own functionally, for example, the SaveGame is our Main API file that contains Save/Load methods.
Here are the files functionally:

  • SaveGame: The Main API
  • SaveGameSettings: The settings and configuration needed to Apply specified API.
  • SaveGameWriter: The Serialization will be done here using BinaryWriter.
  • SaveGameReader: The Deserialization will be done here using BinaryReader.
  • SaveGameTypeManager: This is the type provider that provides support for unsupported types.
  • SaveGameType: We will provide support for unsupported types by extending this class.
  • SaveGameStorage: As the name states, it is our storage, we have different storages for different use cases and different platforms.
  • SaveGameFileStorage: The file storage for supported platforms.
  • SaveGamePlayerPrefsStorage: The PlayerPrefs storage for platforms which don’t support file storage.

So now our project is ready, now we are going to the most important step, Scripting.

Report me back if you have done this step.

Thanks a lot for youre help ! Sorry for my late answer but I’m working all day and I’m working on this project the evening :)!

Okay ! So I did it and I understand all the stuff !
And I already use c# So that is cool ! I’m ready for the next step !

Have a good night !

I will post the next step tomorrow because this step is the longest step.
I will make a tutorial series on how to create a save system after teaching you.

Thanks.
I would be happy to help you.
Have a nice day!

I’m glad you want to teach to other and make a tutorial will be a great idea !
I will follow you and youre work !

So, here is it, the second step of developing Save System in Unity.

Scripting
In this step, we will try to script the most of Save System and develop the Main API.
So, let us start with the API, Open SaveGame file and put the following content in it:
SaveGame.cs

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

namespace SaveSystem
{

    public static class SaveGame
    {

        public static void Save ( string identifier, object value, SaveGameSettings settings )
        {
            if ( string.IsNullOrEmpty ( identifier ) )
            {
                return;
            }
        }

        public static object Load ( string identifier, Type type, object defaultValue, SaveGameSettings settings )
        {
            if ( string.IsNullOrEmpty ( identifier ) || type == null )
            {
                return null;
            }
            if ( defaultValue == null )
            {
                defaultValue = GetDefaultValue ( type );
            }
            return null;
        }

        public static void Clear ( SaveGameSettings settings )
        {
        }

        public static void Move ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
        }

        public static void Copy ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
        }

        public static bool Exists ( string identifier, SaveGameSettings settings )
        {
            return false;
        }

        public static void Delete ( string identifier, SaveGameSettings settings )
        {
        }

        public static string[] GetFiles ( string identifier, SaveGameSettings settings )
        {
            return new string[0];
        }

        public static string[] GetDirectories ( string identifier, SaveGameSettings settings )
        {
            return new string[0];
        }

        public static object GetDefaultValue ( Type type )
        {
            if ( type.IsValueType )
            {
                return Activator.CreateInstance ( type );
            }
            return null;
        }
       
    }

}

This is our Main API, for example, if want to load something, we will use SaveGame.Load to load the specified value using the specified identifier.

Now let us set up the Storage.
The Storage is where we want to store the data and retrieve the data, we have two storage types:

  • PlayerPrefs
  • File

So, open SaveGameStorage file and put the below content in it:
SaveGameStorage.cs

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

namespace SaveSystem
{

    /// <summary>
    /// Save game storage.
    /// </summary>
    public abstract class SaveGameStorage
    {

        public abstract bool HasFileIO { get; }

        public virtual void OnBeforeSave ( string identifier, SaveGameSettings settings )
        {
        }

        public virtual void OnAfterSave ( string identifier, SaveGameSettings settings )
        {
        }

        public virtual void OnBeforeLoad ( string identifier, SaveGameSettings settings )
        {
        }

        public virtual void OnAfterLoad ( string identifier, SaveGameSettings settings )
        {
        }

        public abstract Stream GetReadStream ( string identifier, SaveGameSettings settings );

        public abstract Stream GetWriteStream ( string identifier, SaveGameSettings settings );

        public abstract void Move ( string fromIdentifier, string toIdentifier, SaveGameSettings settings );

        public abstract void Copy ( string fromIdentifier, string toIdentifier, SaveGameSettings settings );

        public abstract bool Exists ( string identifier, SaveGameSettings settings );

        public abstract void Clear ( SaveGameSettings settings );

        public abstract void Delete ( string identifier, SaveGameSettings settings );

        public abstract string[] GetFiles ( string identifier, SaveGameSettings settings );

        public abstract string[] GetDirectories ( string identifier, SaveGameSettings settings );

        public static SaveGameStorage GetAppropriate ()
        {
            #if UNITY_TVOS || UNITY_SAMSUNGTV || UNITY_WEBGL
            return new SaveGamePlayerPrefsStorage ();
            #else
            return new SaveGameFileStorage ();
            #endif
        }

        public static bool IsAppropriate ( SaveGameStorage storage )
        {
            if ( storage == null )
            {
                return false;
            }
            if ( storage.HasFileIO )
            {
                #if UNITY_TVOS || UNITY_SAMSUNGTV || UNITY_WEBGL
                return false;
                #else
                return true;
                #endif
            }
            return true;
        }

    }

}

Now we have the abstract of a Storage, now we can set up other storages on this abstract structure.
But, at first we need to allow the user to specify settings, so open SaveGameSettings file and put the below code in the file:
SaveGameSettings.cs

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

namespace SaveSystem
{

    /// <summary>
    /// Save game settings.
    /// </summary>
    public struct SaveGameSettings
    {
       
        #region Fields

        private static SaveGameSettings m_DefaultSettings;
        private static Encoding m_DefaultEncoding;
        private static SaveGameStorage m_DefaultStorage;
        private Encoding m_Encoding;
        private SaveGameStorage m_Storage;
        private bool m_Overwrite;

        #endregion

        #region Properties

        public static SaveGameSettings DefaultSettings
        {
            get
            {
                return m_DefaultSettings;
            }
            set
            {
                m_DefaultSettings = value;
            }
        }

        public static Encoding DefaultEncoding
        {
            get
            {
                if ( m_DefaultEncoding == null )
                {
                    m_DefaultEncoding = Encoding.UTF8;
                }
                return m_DefaultEncoding;
            }
            set
            {
                m_DefaultEncoding = value;
            }
        }

        public static SaveGameStorage DefaultStorage
        {
            get
            {
                if ( !SaveGameStorage.IsAppropriate ( m_DefaultStorage ) )
                {
                    m_DefaultStorage = SaveGameStorage.GetAppropriate ();
                }
                return m_DefaultStorage;
            }
            set
            {
                m_DefaultStorage = value;
            }
        }

        public Encoding Encoding
        {
            get
            {
                if ( m_Encoding == null )
                {
                    m_Encoding = DefaultEncoding;
                }
                return m_Encoding;
            }
            set
            {
                m_Encoding = value;
            }
        }

        public SaveGameStorage Storage
        {
            get
            {
                if ( !SaveGameStorage.IsAppropriate ( m_Storage ) )
                {
                    m_Storage = DefaultStorage;
                }
                return m_Storage;
            }
            set
            {
                m_Storage = value;
            }
        }

        public bool Overwrite
        {
            get
            {
                return m_Overwrite;
            }
            set
            {
                m_Overwrite = value;
            }
        }

        #endregion

        #region Constructors

        public SaveGameSettings ( Encoding encoding, SaveGameStorage storage )
        {
            m_Encoding = encoding;
            m_Storage = storage;
            m_Overwrite = true;
        }

        #endregion
       
    }

}

So, let us set up the two Storages, and the first one is PlayerPrefs, Open SaveGamePlayerPrefsStorage and put the below content in it:
SaveGamePlayerPrefsStorage.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;

namespace SaveSystem
{

    /// <summary>
    /// Save game player prefs storage.
    /// </summary>
    public class SaveGamePlayerPrefsStorage : SaveGameStorage
    {
       
        protected MemoryStream m_TempStream;

        public override bool HasFileIO
        {
            get
            {
                return false;
            }
        }

        public override void Clear ( SaveGameSettings settings )
        {
            PlayerPrefs.DeleteAll ();
        }

        public override void Copy ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            PlayerPrefs.SetString ( toIdentifier, PlayerPrefs.GetString ( fromIdentifier ) );
        }

        public override void Delete ( string identifier, SaveGameSettings settings )
        {
            PlayerPrefs.DeleteKey ( identifier );
        }

        public override bool Exists ( string identifier, SaveGameSettings settings )
        {
            return PlayerPrefs.HasKey ( identifier );
        }

        public override void Move ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            Copy ( fromIdentifier, toIdentifier, settings );
            Delete ( fromIdentifier, settings );
        }

        public override string[] GetDirectories ( string identifier, SaveGameSettings settings )
        {
            return new string[0];
        }

        public override string[] GetFiles ( string identifier, SaveGameSettings settings )
        {
            return new string[0];
        }

        public override Stream GetReadStream ( string identifier, SaveGameSettings settings )
        {
            return new MemoryStream ( settings.Encoding.GetBytes ( PlayerPrefs.GetString ( identifier ) ) );
        }

        public override Stream GetWriteStream ( string identifier, SaveGameSettings settings )
        {
            m_TempStream = new MemoryStream ();
            return m_TempStream;
        }

        public override void OnAfterSave ( string identifier, SaveGameSettings settings )
        {
            PlayerPrefs.SetString ( identifier, settings.Encoding.GetString ( m_TempStream.ToArray () ) );
            m_TempStream.Dispose ();
        }

    }

}

And the next one is SaveGameFileStorage, so open it and put the below content in it:
SaveGameFileStorage.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;

namespace SaveSystem
{

    /// <summary>
    /// Save game file storage.
    /// </summary>
    public class SaveGameFileStorage : SaveGameStorage
    {

        public override bool HasFileIO
        {
            get
            {
                return true;
            }
        }

        public override void Clear ( SaveGameSettings settings )
        {
            string path = Application.persistentDataPath;
            DirectoryInfo info = new DirectoryInfo ( path );
            DirectoryInfo [] directories = info.GetDirectories ();
            for ( int i = 0; i < directories.Length; i++ )
            {
                if ( directories [ i ].Name != "Unity" )
                {
                    directories [ i ].Delete ( true );
                }
            }
            FileInfo [] files = info.GetFiles ();
            for ( int i = 0; i < files.Length; i++ )
            {
                files [ i ].Delete ();
            }
        }

        public override void Copy ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            string fromPath = GetAbsolutePath ( fromIdentifier );
            string toPath = GetAbsolutePath ( toIdentifier );
            if ( File.Exists ( fromPath ) && !Directory.Exists ( toPath ) )
            {
                File.Copy ( fromPath, toPath, settings.Overwrite );
            }
            else if ( File.Exists ( fromPath ) && Directory.Exists ( toPath ) )
            {
                File.Copy ( fromPath, Path.Combine ( toPath, Path.GetFileName ( fromPath ) ), settings.Overwrite );
            }
            else if ( Directory.Exists ( fromPath ) && !File.Exists ( toPath ) )
            {
                Directory.CreateDirectory ( toPath );
                DirectoryInfo info = new DirectoryInfo ( fromPath );
                FileInfo [] files = info.GetFiles ();
                for ( int i = 0; i < files.Length; i++ )
                {
                    string dest = Path.Combine ( toPath, files [ i ].Name );
                    files [ i ].CopyTo ( dest, settings.Overwrite );
                }
                DirectoryInfo [] directories = info.GetDirectories ();
                for ( int i = 0; i < directories.Length; i++ )
                {
                    string dest = Path.Combine ( toPath, directories [ i ].Name );
                    Copy ( directories [ i ].FullName, dest, settings );
                }
            }
        }

        public override void Delete ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            if ( File.Exists ( path ) )
            {
                File.Delete ( path );
            }
            else if ( Directory.Exists ( path ) )
            {
                Directory.Delete ( path, true );
            }
        }

        public override bool Exists ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            return File.Exists ( path ) || Directory.Exists ( path );
        }

        public override string[] GetDirectories ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            string [] result = new string[0];
            if ( Directory.Exists ( path ) )
            {
                DirectoryInfo info = new DirectoryInfo ( path );
                DirectoryInfo [] directories = info.GetDirectories ();
                result = new string[directories.Length];
                for ( int i = 0; i < directories.Length; i++ )
                {
                    result [ i ] = directories [ i ].Name;
                }
            }
            return result;
        }

        public override string[] GetFiles ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            string [] result = new string[0];
            if ( Directory.Exists ( path ) )
            {
                DirectoryInfo info = new DirectoryInfo ( path );
                FileInfo [] files = info.GetFiles ();
                result = new string[files.Length];
                for ( int i = 0; i < files.Length; i++ )
                {
                    result [ i ] = files [ i ].Name;
                }
            }
            return result;
        }

        public override void Move ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            string fromPath = GetAbsolutePath ( fromIdentifier );
            string toPath = GetAbsolutePath ( toIdentifier );
            if ( File.Exists ( fromPath ) && !Directory.Exists ( toPath ) )
            {
                File.Move ( fromPath, toPath );
            }
            else if ( File.Exists ( fromPath ) && Directory.Exists ( toPath ) )
            {
                File.Move ( fromPath, Path.Combine ( toPath, Path.GetFileName ( fromPath ) ) );
            }
            else if ( Directory.Exists ( fromPath ) && !File.Exists ( toPath ) )
            {
                Directory.Move ( fromPath, toPath );
            }
        }

        public override Stream GetReadStream ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            if ( !File.Exists ( path ) )
            {
                return null;
            }
            return new FileStream ( path, FileMode.Open );
        }

        public override Stream GetWriteStream ( string identifier, SaveGameSettings settings )
        {
            string path = GetAbsolutePath ( identifier );
            string dirName = Path.GetDirectoryName ( path );
            if ( !Directory.Exists ( dirName ) )
            {
                Directory.CreateDirectory ( dirName );
            }
            return new FileStream ( path, FileMode.Create );
        }

        public static string GetAbsolutePath ( string identifier )
        {
            if ( Path.IsPathRooted ( identifier ) )
            {
                return identifier;
            }
            else
            {
                return Path.Combine ( Application.persistentDataPath, identifier );
            }
        }
       
    }

}

Now, our storages are ready and we can use them in serialization, but first, let us set up Supported Types, so open SaveGameType and put the below content in it:
SaveGameType.cs

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

namespace SaveSystem
{

    public abstract class SaveGameType
    {
       
        public abstract Type AssociatedType { get; }

        public abstract void Write ( object value, SaveGameWriter writer );

        public abstract object Read ( SaveGameReader reader );

        public abstract void ReadInto ( object value, SaveGameReader reader );
       
    }

}

And then open SaveGameTypeManager and then put the below content in it:
SaveGameTypeManager.cs

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

namespace SaveSystem
{

    /// <summary>
    /// Save game type manager.
    /// </summary>
    public static class SaveGameTypeManager
    {
       
        private static Dictionary<Type, SaveGameType> m_Types;

        public static Dictionary<Type, SaveGameType> Types
        {
            get
            {
                if ( m_Types == null )
                {
                    Initialize ();
                }
                return m_Types;
            }
        }

        [RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType.BeforeSceneLoad )]
        public static void Initialize ()
        {
            m_Types = new Dictionary<Type, SaveGameType> ();
            // TODO: Add Supported Types
        }

        public static void AddType ( SaveGameType saveGameType )
        {
            if ( saveGameType == null )
            {
                return;
            }
            AddType ( saveGameType.AssociatedType, saveGameType );
        }

        public static void AddType ( Type type, SaveGameType saveGameType )
        {
            if ( type == null || saveGameType == null )
            {
                return;
            }
            Types.Add ( type, saveGameType );
        }

        public static void RemoveType ( Type type )
        {
            if ( type == null || !HasType ( type ) )
            {
                return;
            }
            Types.Remove ( type );
        }

        public static bool HasType ( Type type )
        {
            if ( type == null )
            {
                return false;
            }
            return Types.ContainsKey ( type );
        }

        public static SaveGameType GetType ( Type type )
        {
            SaveGameType saveGameType;
            if ( type == null || !HasType ( type ) )
            {
                saveGameType = null;
            }
            else
            {
                Types.TryGetValue ( type, out saveGameType );
            }
            return saveGameType;
        }
       
    }

}

All done, now the storages, API and the settings are ready and we can now set up the Serialization.

So open SaveGameWriter file and put the below content in it:
SaveGameWriter.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using UnityEngine;

namespace SaveSystem
{

    public class SaveGameWriter : IDisposable
    {
       
        #region Fields

        protected BinaryWriter m_Writer;
        protected SaveGameSettings m_Settings;

        #endregion

        #region Properties

        public virtual BinaryWriter Writer
        {
            get
            {
                return m_Writer;
            }
        }

        public virtual SaveGameSettings Settings
        {
            get
            {
                return m_Settings;
            }
            set
            {
                m_Settings = value;
            }
        }

        #endregion

        #region Constructors

        public SaveGameWriter ( Stream stream ) : this ( stream, SaveGameSettings.DefaultSettings )
        {
        }

        public SaveGameWriter ( BinaryWriter writer ) : this ( writer, SaveGameSettings.DefaultSettings )
        {
        }

        public SaveGameWriter ( Stream stream, SaveGameSettings settings ) : this ( new BinaryWriter (
                stream,
                settings.Encoding ), settings )
        {
        }

        public SaveGameWriter ( BinaryWriter writer, SaveGameSettings settings )
        {
            m_Writer = writer;
            m_Settings = settings;
        }

        #endregion

        #region Methods

        public virtual void Write<T> ( T value )
        {
            Write ( ( object )value );
        }

        public virtual void Write ( object value )
        {
            if ( value == null )
            {
                m_Writer.Write ( "null" );
            }
            else
            {
                Type type = value.GetType ();
                if ( SaveGameTypeManager.HasType ( type ) )
                {
                    SaveGameTypeManager.GetType ( type ).Write ( value, this );
                }
                else if ( type.IsPrimitive )
                {
                    if ( type == typeof ( short ) )
                    {
                        m_Writer.Write ( ( short )value );
                    }
                    else if ( type == typeof ( int ) )
                    {
                        m_Writer.Write ( ( int )value );
                    }
                    else if ( type == typeof ( long ) )
                    {
                        m_Writer.Write ( ( long )value );
                    }
                    else if ( type == typeof ( ushort ) )
                    {
                        m_Writer.Write ( ( ushort )value );
                    }
                    else if ( type == typeof ( uint ) )
                    {
                        m_Writer.Write ( ( uint )value );
                    }
                    else if ( type == typeof ( ulong ) )
                    {
                        m_Writer.Write ( ( ulong )value );
                    }
                    else if ( type == typeof ( double ) )
                    {
                        m_Writer.Write ( ( double )value );
                    }
                    else if ( type == typeof ( float ) )
                    {
                        m_Writer.Write ( ( float )value );
                    }
                    else if ( type == typeof ( byte ) )
                    {
                        m_Writer.Write ( ( byte )value );
                    }
                    else if ( type == typeof ( sbyte ) )
                    {
                        m_Writer.Write ( ( sbyte )value );
                    }
                    else if ( type == typeof ( char ) )
                    {
                        m_Writer.Write ( ( char )value );
                    }
                    else if ( type == typeof ( bool ) )
                    {
                        m_Writer.Write ( ( bool )value );
                    }
                }
                else if ( type == typeof ( string ) )
                {
                    m_Writer.Write ( ( string )value );
                }
                else if ( type == typeof ( decimal ) )
                {
                    m_Writer.Write ( ( decimal )value );
                }
                else if ( type.IsEnum )
                {
                    m_Writer.Write ( value.ToString () );
                }
                else if ( type == typeof ( DateTime ) )
                {
                    m_Writer.Write ( ( ( DateTime )value ).ToBinary () );
                }
                else if ( type == typeof ( TimeSpan ) )
                {
                    m_Writer.Write ( ( ( TimeSpan )value ).ToString () );
                }
                else if ( type.IsArray )
                {
                    Array array = value as Array;
                    m_Writer.Write ( array.Rank );
                    for ( int i = 0; i < array.Rank; i++ )
                    {
                        m_Writer.Write ( array.GetLength ( i ) );
                    }
                    int [] indices = new int[array.Rank];
                    for ( int i = 0; i < array.Rank; i++ )
                    {
                        for ( int j = 0; j < array.GetLength ( i ); j++ )
                        {
                            indices [ i ] = j;
                            Write ( array.GetValue ( indices ) );
                        }
                    }
                }
                else if ( value is IEnumerable && ( value is ICollection || type.GetInterface ( "ICollection`1" ) != null ) )
                {
                    IEnumerable enumerable = value as IEnumerable;
                    int count = ( int )type.GetProperty ( "Count" ).GetValue ( value, null );
                    IEnumerator e = enumerable.GetEnumerator ();
                    m_Writer.Write ( count );
                    while ( e.MoveNext () )
                    {
                        Write ( e.Current );
                    }
                }
                else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( KeyValuePair<,> ) )
                {
                    Write ( type.GetProperty ( "Key" ).GetValue ( value, BindingFlags.Default, null, null, null ) );
                    Write ( type.GetProperty ( "Value" ).GetValue ( value, BindingFlags.Default, null, null, null ) );
                }
                else if ( value is ISerializable )
                {
                    SerializationInfo info = new SerializationInfo ( type, new FormatterConverter () );
                    ISerializable serializable = value as ISerializable;
                    serializable.GetObjectData ( info, new StreamingContext ( StreamingContextStates.All ) );
                    var e = info.GetEnumerator ();
                    while ( e.MoveNext () )
                    {
                        Write ( e.Name );
                        Write ( e.Value );
                    }
                }
                else
                {
                    FieldInfo [] fields = type.GetFields ().Where ( field => !field.IsLiteral && !field.IsNotSerialized && !field.IsStatic ).ToArray ();
                    PropertyInfo [] properties = type.GetProperties ().Where ( property => property.CanRead && property.CanRead ).ToArray ();
                    m_Writer.Write ( fields.Length );
                    for ( int i = 0; i < fields.Length; i++ )
                    {
                        m_Writer.Write ( fields [ i ].Name );
                        Write ( fields [ i ].GetValue ( value ) );
                    }
                    m_Writer.Write ( properties.Length );
                    for ( int i = 0; i < properties.Length; i++ )
                    {
                        m_Writer.Write ( properties [ i ].Name );
                        Write ( properties [ i ].GetValue ( value, BindingFlags.Default, null, null, null ) );
                    }
                }
            }
        }

        public virtual void Dispose ()
        {
            m_Writer.Close ();
        }

        #endregion

    }

}

And then open SaveGameReader and put the below content in it:
SaveGameReader.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using UnityEngine;

namespace SaveSystem
{

    public class SaveGameReader : IDisposable
    {

        #region Fields

        protected BinaryReader m_Reader;
        protected SaveGameSettings m_Settings;

        #endregion

        #region Properties

        public virtual BinaryReader Reader
        {
            get
            {
                return m_Reader;
            }
        }

        public virtual SaveGameSettings Settings
        {
            get
            {
                return m_Settings;
            }
            set
            {
                m_Settings = value;
            }
        }

        #endregion

        #region Constructors

        public SaveGameReader ( Stream stream ) : this ( stream, SaveGameSettings.DefaultSettings )
        {
        }

        public SaveGameReader ( BinaryReader reader ) : this ( reader, SaveGameSettings.DefaultSettings )
        {
        }

        public SaveGameReader ( Stream stream, SaveGameSettings settings ) : this ( new BinaryReader (
                stream,
                settings.Encoding ), settings )
        {
        }

        public SaveGameReader ( BinaryReader reader, SaveGameSettings settings )
        {
            m_Reader = reader;
            m_Settings = settings;
        }

        #endregion

        #region Methods

        public virtual T Read<T> ()
        {
            return ( T )Read ( typeof ( T ) );
        }

        public virtual object Read ( Type type )
        {
            object result = null;
            if ( type == null )
            {
                result = null;
            }
            else if ( SaveGameTypeManager.HasType ( type ) )
            {
                return SaveGameTypeManager.GetType ( type ).Read ( this );
            }
            else if ( type.IsPrimitive )
            {
                if ( type == typeof ( short ) )
                {
                    result = m_Reader.ReadInt16 ();
                }
                else if ( type == typeof ( int ) )
                {
                    result = m_Reader.ReadInt32 ();
                }
                else if ( type == typeof ( long ) )
                {
                    result = m_Reader.ReadInt64 ();
                }
                else if ( type == typeof ( ushort ) )
                {
                    result = m_Reader.ReadUInt16 ();
                }
                else if ( type == typeof ( uint ) )
                {
                    result = m_Reader.ReadUInt32 ();
                }
                else if ( type == typeof ( ulong ) )
                {
                    result = m_Reader.ReadUInt64 ();
                }
                else if ( type == typeof ( double ) )
                {
                    result = m_Reader.ReadDouble ();
                }
                else if ( type == typeof ( float ) )
                {
                    result = m_Reader.ReadSingle ();
                }
                else if ( type == typeof ( byte ) )
                {
                    result = m_Reader.ReadByte ();
                }
                else if ( type == typeof ( sbyte ) )
                {
                    result = m_Reader.ReadSByte ();
                }
                else if ( type == typeof ( char ) )
                {
                    result = m_Reader.ReadChar ();
                }
                else if ( type == typeof ( bool ) )
                {
                    result = m_Reader.ReadBoolean ();
                }
            }
            else if ( type == typeof ( string ) )
            {
                result = m_Reader.ReadString ();
            }
            else if ( type == typeof ( decimal ) )
            {
                result = m_Reader.ReadDecimal ();
            }
            else if ( type.IsEnum )
            {
                result = Enum.Parse ( type, m_Reader.ReadString () );
            }
            else if ( type == typeof ( DateTime ) )
            {
                DateTime.FromBinary ( m_Reader.ReadInt64 () );
            }
            else if ( type == typeof ( TimeSpan ) )
            {
                TimeSpan.Parse ( m_Reader.ReadString () );
            }
            else if ( type.IsArray )
            {
                Type elementType = type.GetElementType ();
                int rank = m_Reader.ReadInt32 ();
                int [] lengths = new int[rank];
                for ( int i = 0; i < rank; i++ )
                {
                    lengths [ i ] = m_Reader.ReadInt32 ();
                }
                Array array = Array.CreateInstance ( elementType, lengths );
                int [] indices = new int[rank];
                for ( int i = 0; i < rank; i++ )
                {
                    for ( int j = 0; j < lengths [ i ]; j++ )
                    {
                        indices [ i ] = j;
                        array.SetValue ( Read ( elementType ), indices );
                    }
                }
                result = array;
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( KeyValuePair<,> ) )
            {
                result = Activator.CreateInstance ( type );
                PropertyInfo key = type.GetProperty ( "Key" );
                PropertyInfo value = type.GetProperty ( "Value" );
                key.SetValue ( result, Read ( key.PropertyType ), BindingFlags.Default, null, null, null );
                value.SetValue (
                    result,
                    Read ( value.PropertyType ),
                    BindingFlags.Default,
                    null,
                    null,
                    null );
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( List<> ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                IList list = ( IList )Activator.CreateInstance ( type );
                int length = m_Reader.ReadInt32 ();
                for ( int i = 0; i < length; i++ )
                {
                    list.Add ( Read ( genericArgs [ 0 ] ) );
                }
                result = list;
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( LinkedList<> ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                object linkedList = Activator.CreateInstance ( type );
                MethodInfo addLast = type.GetMethod ( "AddLast", genericArgs );
                int length = m_Reader.ReadInt32 ();
                for ( int i = 0; i < length; i++ )
                {
                    addLast.Invoke ( linkedList, new object[] { Read ( genericArgs [ 0 ] ) } );
                }
                result = linkedList;
            }
            else if ( type.IsGenericType && ( type.GetGenericTypeDefinition () == typeof ( Dictionary<,> ) ||
                      type.GetGenericTypeDefinition () == typeof ( SortedDictionary<,> ) ||
                      type.GetGenericTypeDefinition () == typeof ( SortedList<,> ) ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                IDictionary dictionary = ( IDictionary )Activator.CreateInstance ( type );
                int length = m_Reader.ReadInt32 ();
                Type keyValuePairType = typeof ( KeyValuePair<,> ).MakeGenericType ( genericArgs );
                PropertyInfo keyProperty = keyValuePairType.GetProperty ( "Key" );
                PropertyInfo valueProperty = keyValuePairType.GetProperty ( "Value" );
                for ( int i = 0; i < length; i++ )
                {
                    object keyValuePair = Read ( keyValuePairType );
                    object key = keyProperty.GetValue ( keyValuePair, BindingFlags.Default, null, null, null );
                    object value = valueProperty.GetValue ( keyValuePair, BindingFlags.Default, null, null, null );
                    dictionary.Add ( key, value );
                }
                result = dictionary;
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( Stack<> ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                object stack = Activator.CreateInstance ( type );
                MethodInfo push = type.GetMethod ( "Push" );
                int length = m_Reader.ReadInt32 ();
                for ( int i = 0; i < length; i++ )
                {
                    push.Invoke ( stack, new object[] { Read ( genericArgs [ 0 ] ) } );
                }
                result = stack;
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( Queue<> ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                object queue = Activator.CreateInstance ( type );
                MethodInfo enqueue = type.GetMethod ( "Enqueue" );
                int length = m_Reader.ReadInt32 ();
                for ( int i = 0; i < length; i++ )
                {
                    enqueue.Invoke ( queue, new object[] { Read ( genericArgs [ 0 ] ) } );
                }
                result = queue;
            }
            else if ( type.IsGenericType && type.GetGenericTypeDefinition () == typeof ( HashSet<> ) )
            {
                Type [] genericArgs = type.GetGenericArguments ();
                object hashSet = Activator.CreateInstance ( type );
                MethodInfo addMethod = type.GetMethod ( "Add" );
                int length = m_Reader.ReadInt32 ();
                for ( int i = 0; i < length; i++ )
                {
                    addMethod.Invoke ( hashSet, new object[] { Read ( genericArgs [ 0 ] ) } );
                }
                result = hashSet;
            }
            else
            {
                if ( type.IsValueType )
                {
                    result = Activator.CreateInstance ( type );
                }
                else
                {
                    result = FormatterServices.GetUninitializedObject ( type );
                }
                if ( result != null )
                {
                    int length = m_Reader.ReadInt32 ();
                    for ( int i = 0; i < length; i++ )
                    {
                        string name = m_Reader.ReadString ();
                        FieldInfo field = type.GetField ( name );
                        if ( field != null )
                        {
                            field.SetValue ( result, Read ( field.FieldType ) );
                        }
                    }
                    length = m_Reader.ReadInt32 ();
                    for ( int i = 0; i < length; i++ )
                    {
                        string name = m_Reader.ReadString ();
                        PropertyInfo property = type.GetProperty ( name );
                        if ( property != null )
                        {
                            property.SetValue ( result, Read ( property.PropertyType ), BindingFlags.Default, null, null, null );
                        }
                    }
                }
            }
            if ( result is IDeserializationCallback )
            {
                ( result as IDeserializationCallback ).OnDeserialization ( this );
            }
            return result;
        }

        public virtual void ReadInto ( object value )
        {
            if ( value == null )
            {
                return;
            }
            Type type = value.GetType ();
            if ( type.IsArray )
            {
                Array array = value as Array;
                m_Reader.ReadInt32 ();
                for ( int i = 0; i < array.Rank; i++ )
                {
                    m_Reader.ReadInt32 ();
                }
                int [] indices = new int[array.Rank];
                for ( int i = 0; i < array.Rank; i++ )
                {
                    for ( int j = 0; j < array.GetLength ( i ); j++ )
                    {
                        indices [ i ] = j;
                        ReadInto ( array.GetValue ( indices ) );
                    }
                }
            }
            else if ( value is ICollection && value is IEnumerable )
            {
                m_Reader.ReadInt32 ();
                IEnumerable e = value as IEnumerable;
                foreach ( object subValue in e )
                {
                    ReadInto ( subValue );
                }
            }
            else if ( SaveGameTypeManager.HasType ( type ) )
            {
                SaveGameTypeManager.GetType ( type ).ReadInto ( value, this );
            }
        }

        public virtual void Dispose ()
        {
            m_Reader.Close ();
        }

        #endregion
       
    }

}

Nice work, now our Serialization is ready and we can serialize collections, simple types and some complex types using this serialization.

Now go back and open SaveGame file to complete the API:
SaveGame.cs

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

namespace SaveSystem
{

    public static class SaveGame
    {

        public static void Save ( string identifier, object value, SaveGameSettings settings )
        {
            if ( string.IsNullOrEmpty ( identifier ) )
            {
                return;
            }
            settings.Storage.OnBeforeSave ( identifier, settings );
            Stream stream = settings.Storage.GetWriteStream ( identifier, settings );
            using ( SaveGameWriter writer = new SaveGameWriter ( stream, settings ) )
            {
                writer.Write ( value );
            }
            settings.Storage.OnAfterSave ( identifier, settings );
        }

        public static object Load ( string identifier, Type type, object defaultValue, SaveGameSettings settings )
        {
            if ( string.IsNullOrEmpty ( identifier ) || type == null )
            {
                return null;
            }
            if ( defaultValue == null )
            {
                defaultValue = GetDefaultValue ( type );
            }
            settings.Storage.OnBeforeLoad ( identifier, settings );
            Stream stream = settings.Storage.GetReadStream ( identifier, settings );
            object result = null;
            using ( SaveGameReader reader = new SaveGameReader ( stream, settings ) )
            {
                result = reader.Read ( type );
            }
            settings.Storage.OnAfterLoad ( identifier, settings );
            if ( result == null )
            {
                result = defaultValue;
            }
            return result;
        }

        public static void Clear ( SaveGameSettings settings )
        {
            settings.Storage.Clear ( settings );
        }

        public static void Move ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            settings.Storage.Move ( fromIdentifier, toIdentifier, settings );
        }

        public static void Copy ( string fromIdentifier, string toIdentifier, SaveGameSettings settings )
        {
            settings.Storage.Copy ( fromIdentifier, toIdentifier, settings );
        }

        public static bool Exists ( string identifier, SaveGameSettings settings )
        {
            return settings.Storage.Exists ( identifier, settings );
        }

        public static void Delete ( string identifier, SaveGameSettings settings )
        {
            settings.Storage.Delete ( identifier, settings );
        }

        public static string[] GetFiles ( string identifier, SaveGameSettings settings )
        {
            return settings.Storage.GetFiles ( identifier, settings );
        }

        public static string[] GetDirectories ( string identifier, SaveGameSettings settings )
        {
            return settings.Storage.GetDirectories ( identifier, settings );
        }

        public static object GetDefaultValue ( Type type )
        {
            if ( type.IsValueType )
            {
                return Activator.CreateInstance ( type );
            }
            return null;
        }
       
    }

}

All done, now we can use SaveGame API to save and load data.

So make an example script and try it.

In the next step, We will develop Supported Types and we will add GameObject serialization.

Excuse me if I didn’t provide any explanation for codes because there is not the right place for education, so provided them as simple as is.

Hope this helps.
Thanks.

Oh ! Big step ! Thanks for all of this !

So i understand what is what script , but how can i use save a load ?
Do you have some exemple please ?

Here is a test script to try save & load:
Test.cs

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

using SaveSystem;

public class Test : MonoBehaviour
{

    // Use this for initialization
    void Start ()
    {
        SaveGame.Save ( "helloWorld.bin", "Hello World", new SaveGameSettings () );
        Debug.Log ( SaveGame.Load ( "helloWorld.bin", typeof ( string ), "No Hello World", new SaveGameSettings () ) );
    }
   
    // Update is called once per frame
    void Update ()
    {
       
    }
   
}

You can update SaveGame API and provide some easier and faster overloads for Save and Load methods.

Hope this helps.
Thanks.