Search Unity

Question How can I update a json save game when changing a gameobject in the hierarchy ?

Discussion in 'Scripting' started by shamenraze1988, Jun 30, 2021.

  1. shamenraze1988

    shamenraze1988

    Joined:
    Nov 24, 2020
    Posts:
    208
    The script/s a bit long but all connected and I will explain where is the problem.

    With this script SaveLoad I just save and load the data from text files using json. The problem is at the Load method at line 97 :

    Code (csharp):
    1.  
    2. var objectToSetState = uniqueIdToObject[saveObject.gameObjectUniqueID];
    3.  
    The variable uniqueIdToObject contains two items :



    but the variable saveGame.saveObjects contains 3 items and each item have his own uniqueIdToObject:



    When I'm using the Save method to save the data to a json file for example if I had on the hierarchy 3 objects with the GenerateGuid on them :

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6.  
    7. public class GenerateGuid : MonoBehaviour
    8. {
    9.    public string uniqueGuidID;
    10.  
    11.    private Guid guidID;
    12.  
    13.    public void GenerateGuidNum()
    14.    {
    15.        guidID = Guid.NewGuid();
    16.        uniqueGuidID = guidID.ToString();
    17.    }
    18. }
    19.  
    Then in the json file I have data of three objects but then for example I deleted one of the objects from the hierarchy or removed from one of the object the script GenerateGuid then the SaveLoad script doesn't know I deleted anything and he read from the json file the whole data of three items but in fact there are only two items now or three items but one of them without the GenerateGuid on it.

    The question is what should I do in that case ? Should I update somehow the json file when the user delete or change on the hierarchy one of the objects ?

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.IO;
    6. using System.Linq;
    7. using UnityEngine;
    8. using UnityEngine.SceneManagement;
    9. using UnityEngine.UI;
    10.  
    11. public class SaveLoad : MonoBehaviour
    12. {
    13.     public FadeInOutSaveGameText fadeInOutSaveGame;
    14.     public float timeToStartSaving;
    15.     public float savingFadeInOutTime;
    16.  
    17.     private List<GameObject> objectsToSave;
    18.     private string saveString;
    19.  
    20.     private void Awake()
    21.     {
    22.         SaveSystem.Init();
    23.  
    24.         if (objectsToSave == null)
    25.         {
    26.             objectsToSave = new List<GameObject>();
    27.         }
    28.  
    29.         Debug.Log("Start");
    30.  
    31.         for (int i = 0; i < objectsToSave.Count; i++)
    32.         {
    33.             Debug.Log($"{i}");
    34.             Debug.Log($"{objectsToSave[i].name}");
    35.         }
    36.  
    37.         Debug.Log("End Init");
    38.     }
    39.  
    40.     public void Save(string Folder, string FileName)
    41.     {
    42.         UpdateObjectsToSave();
    43.  
    44.         SaveGame saveGame = new SaveGame();
    45.         saveGame.saveObjects = new List<SaveObject>();
    46.         for (int i = 0; i < objectsToSave.Count; i++)
    47.         {
    48.             SaveObject saveObject = new SaveObject();
    49.             saveObject.transformSaver = new TransformSaver();
    50.          
    51.             Debug.Log($"{i}");
    52.             Debug.Log($"{objectsToSave[i].name}");
    53.             saveObject.gameObjectUniqueID = objectsToSave[i].GetComponent<GenerateGuid>().uniqueGuidID;
    54.             var x = objectsToSave[i].GetComponents<Component>();
    55.             var stateQueryComponent = x.Where(component => component is IStateQuery).ToList();
    56.             List<KeyToValue> componentsState = new List<KeyToValue>();
    57.             foreach (var z in stateQueryComponent)
    58.             {
    59.                 var w = z as IStateQuery;
    60.                 componentsState.Add(new KeyToValue(w.UniqueId.ToString(), w.GetState()));
    61.             }
    62.  
    63.             saveObject.transformSaver.position = objectsToSave[i].transform.position;
    64.             saveObject.transformSaver.rotation = objectsToSave[i].transform.rotation;
    65.             saveObject.transformSaver.scaling = objectsToSave[i].transform.localScale;
    66.  
    67.             saveObject.componentsState = componentsState;
    68.             saveGame.saveObjects.Add(saveObject);
    69.         }
    70.  
    71.         string json = JsonUtility.ToJson(saveGame);
    72.  
    73.         if (Folder == null && FileName == null)
    74.         {
    75.             SaveSystem.Save(json);
    76.         }
    77.         else
    78.         {
    79.             SaveSystem.Save(Folder, FileName, json);
    80.         }
    81.     }
    82.  
    83.     public void Load(string Folder, string FileName)
    84.     {
    85.         UpdateObjectsToSave();
    86.  
    87.         Dictionary<string, GameObject> uniqueIdToObject = objectsToSave
    88.             .ToDictionary(o => o.GetComponent<GenerateGuid>().uniqueGuidID, o => o);
    89.  
    90.         saveString = SaveSystem.Load(Folder, FileName);
    91.  
    92.         if (saveString != null)
    93.         {
    94.             SaveGame saveGame = JsonUtility.FromJson<SaveGame>(saveString);
    95.             foreach (var saveObject in saveGame.saveObjects)
    96.             {
    97.                 List<KeyToValue> loadedComponents = saveObject.componentsState;
    98.                 var objectToSetState = uniqueIdToObject[saveObject.gameObjectUniqueID];
    99.  
    100.                 objectToSetState.transform.position = saveObject.transformSaver.position;
    101.                 objectToSetState.transform.rotation = saveObject.transformSaver.rotation;
    102.                 objectToSetState.transform.localScale = saveObject.transformSaver.scaling;
    103.  
    104.                 var y = objectToSetState.GetComponents<Component>();
    105.                 var z = y.Where(component => component is IStateQuery).ToList();
    106.                 Dictionary<string, IStateQuery> zz = z.ToDictionary(sq => (sq as IStateQuery).UniqueId.ToString(), sq => sq as IStateQuery);
    107.  
    108.                 foreach (KeyToValue keyvalue in loadedComponents)
    109.                 {
    110.                     zz[keyvalue.Key].SetState(keyvalue.Value);
    111.                 }
    112.             }
    113.         }
    114.     }
    115.  
    116.     private void UpdateObjectsToSave()
    117.     {
    118.         var objectsWithGenerateGuid = GameObject.FindObjectsOfType<GenerateGuid>().ToList();
    119.         objectsToSave = new List<GameObject>();
    120.         if (objectsWithGenerateGuid.Count > 0 && objectsToSave.Count == 0)
    121.         {
    122.             for (int i = 0; i < objectsWithGenerateGuid.Count; i++)
    123.             {
    124.                 objectsToSave.Add(objectsWithGenerateGuid[i].gameObject);
    125.             }
    126.         }
    127.     }
    128.  
    129.     public IEnumerator AuatomaticSaveWithTime()
    130.     {
    131.         yield return new WaitForSeconds(timeToStartSaving);
    132.  
    133.         Save(null, null);
    134.  
    135.         StartCoroutine(fadeInOutSaveGame.OverAllTime(savingFadeInOutTime));
    136.     }
    137.  
    138.     public IEnumerator SaveWithTime(string Folder, string FileName)
    139.     {
    140.         yield return new WaitForSeconds(timeToStartSaving);
    141.  
    142.         Save(Folder, FileName);
    143.  
    144.         StartCoroutine(fadeInOutSaveGame.OverAllTime(savingFadeInOutTime));
    145.     }
    146. }
    147.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,744
    Generally changing the shape of your game save data invalidates previous save games.

    You can explicitly guard against this by including a version number that must match or else it prohibits loading.

    You can implicitly guard against errors by quietly disregarding parts of the JSON that no longer apply.
     
    shamenraze1988 likes this.
  3. shamenraze1988

    shamenraze1988

    Joined:
    Nov 24, 2020
    Posts:
    208

    The unique id is not enough? For example when loading a saved game in the Load method to make a comparison between the uniqueIdToObject and the saveGame.saveObjects and then if it's not the same to update the file by removing the part of the missing id.

    To compare the id's between the uniqueIdToObject and the saveGame.saveObjects.

    This is an example of saved game content :

    {"saveObjects":[{"componentsState":[],"gameObjectUniqueID":"1a6f2068-1676-468b-98b3-b1cc91c68ee2","transformSaver":{"position":{"x":336.0,"y":-34.0,"z":200.0},"scaling":{"x":10.0,"y":10.0,"z":10.0},"rotation":{"x":0.0,"y":0.0,"z":0.0,"w":1.0}}},{"componentsState":[],"gameObjectUniqueID":"1fa5f973-8b02-427c-a924-eb67760e78fa","transformSaver":{"position":{"x":11.0,"y":78.0,"z":291.0},"scaling":{"x":10.0,"y":10.0,"z":10.0},"rotation":{"x":0.0,"y":0.0,"z":0.0,"w":1.0}}}]}

    So when I'm calling the Load method maybe I can make a comparison and then remove the part in the file of the missing id than to write to the file again and then continue with the code in the Load.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,744
    Did I say that? I actually wrote this:

    If you are loading and see GUID 123456 in the save data and you have no idea what GUID 123456 is, what is your strategy? Crash? Disregard it? Give the player some other random GUID?

    My text above suggests "quietly disregarding."

    You can also noisily disregard it, as in throw up warnings and error messages, perhaps even send off application tracking events if you are using analytics.

    I suppose you could amend and re-write the save data right then... why not leave it until the next time you save? If you're disregarding invalid GUIDs once, you can do it again.