How to implement browser saving in Unity

Viewed 764

I don't know how to save data of an online game but I have already implemented saving in a desktop application. How would it be possible to save modified data in my Unity game played on a browser such that it could be loaded to restore the original state? I already have a saving system script:-

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace GameDevTV.Saving
{
    /// <summary>
    /// This component provides the interface to the saving system. It provides
    /// methods to save and restore a scene.
    ///
    /// This component should be created once and shared between all subsequent scenes.
    /// </summary>
    public class SavingSystem : MonoBehaviour
    {
        /// <summary>
        /// Will load the last scene that was saved and restore the state. This
        /// must be run as a coroutine.
        /// </summary>
        /// <param name="saveFile">The save file to consult for loading.</param>
        private void Update()
        {

        }

        public IEnumerator LoadLastScene(string saveFile)
        {
            Dictionary<string, object> state = LoadFile(saveFile);
            int buildIndex = SceneManager.GetActiveScene().buildIndex;
            if (state.ContainsKey("lastSceneBuildIndex"))
            {
                buildIndex = (int)state["lastSceneBuildIndex"];
            }
            yield return SceneManager.LoadSceneAsync(buildIndex);
            RestoreState(state);
        }

        /// <summary>
        /// Save the current scene to the provided save file.
        /// </summary>
        public void Save(string saveFile)
        {
            Dictionary<string, object> state = LoadFile(saveFile);
            CaptureState(state);
            SaveFile(saveFile, state);
        }

        /// <summary>
        /// Delete the state in the given save file.
        /// </summary>
        public void Delete(string saveFile)
        {
            File.Delete(GetPathFromSaveFile(saveFile));
        }

        // PRIVATE

        private void Load(string saveFile)
        {
            RestoreState(LoadFile(saveFile));
        }

        private Dictionary<string, object> LoadFile(string saveFile)
        {
            string path = GetPathFromSaveFile(saveFile);
            if (!File.Exists(path))
            {
                return new Dictionary<string, object>();
            }
            using (FileStream stream = File.Open(path, FileMode.Open))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                return (Dictionary<string, object>)formatter.Deserialize(stream);
            }
        }

        private void SaveFile(string saveFile, object state)
        {
            PlayerPrefs.Save();
            string path = GetPathFromSaveFile(saveFile);
            print("Saving to " + path);
            using (FileStream stream = File.Open(path, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, state);
            }
        }

        private void CaptureState(Dictionary<string, object> state)
        {
            foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
            {
                state[saveable.GetUniqueIdentifier()] = saveable.CaptureState();
            }

            state["lastSceneBuildIndex"] = SceneManager.GetActiveScene().buildIndex;
        }

        private void RestoreState(Dictionary<string, object> state)
        {
            foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
            {
                string id = saveable.GetUniqueIdentifier();
                if (state.ContainsKey(id))
                {
                    saveable.RestoreState(state[id]);
                }
            }
        }

        private string GetPathFromSaveFile(string saveFile)
        {
            return Path.Combine(Application.persistentDataPath, saveFile + ".sav");
        }
    }
}

The current saving system can save to a desktop location but im not sure if this game running online could possibly save to the desktop? Any suggestions on how to modify this code to make it save from a browser?

2 Answers

I believe you could save everything in a browser cache, but a user could delete it by mistake (I've never actually done this).
What I would actually try to do is save stuff in an external database, hosted somewhere. If the game is online, maybe it requires authentication, which would allow you to distinguish between what got saved from whom.
There might also be other alternatives, but I haven't really messed with browser games in Unity.

An easy approach but not very reliable can be to just store it in the persistent data path. Please note that this process is not immediately flushed to the IndexedDB and you should use: Application.ExternalEval("_JS_FileSystem_Sync();");

Another possible solution is the following: You should generate your final save data as a string, one way to do this is by using JSON serialization. You should have a server and a sort of login system to identify the user and with PHP you can then associate in a DB that user's save data string.

The second solution is a little bit more complicated, but it is indeed one of the most robust I can think of for WebGL. You have multiple of websites that offer these services like 1&1.
If you rather prefer trying first free alternatives you can use sites like 000webhost to store your webgGL app + all PHP functionalities + the databases to store the info.

Related