- Home /
Having a problem trying to save data
Hey guys,
I'm having an issue trying to implement save/load functionality into my game.
I have a test scene in which a cube can move around with the WASD keys. F5 saves the cube's location as well as a few other variables to a .json file, and F9 loads that saved data. So if you hit F9 to load, the cube will go back to the position it was at when you last hit F5 to save. And it works.
The problem I'm having is this: when I try to replicate the process, the data doesn't get saved. I just get a .json file with nothing in it. First I tried it in another project. Didn't work. I went back to my test project, and created a new scene. I took all of the game objects, which are all prefabs, and put them in the new scene. To the best of my knowledge, both scenes and everything in them, are identical. And it still doesn't work in the second scene.
I have an empty game object called GameGlobals that has the following script attached:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameGlobals : MonoBehaviour
{
public static GameObject Player;
public static Player ref_Player;
public static SaveData ref_SaveData;
public static SaveLoad ref_SaveLoad;
}
I have a cube called Player that has the following script attached:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public string playerName = "Rigel One";
public Vector3 playerLocation;
public float playerHealth = 100;
public int playerAmmo = 40;
public float moveSpeed;
void Awake()
{
GameGlobals.Player = this.gameObject;
GameGlobals.ref_Player = this;
}
void Update ()
{
transform.Translate(moveSpeed * Input.GetAxis("Horizontal") * Time.deltaTime, 0f, moveSpeed * Input.GetAxis("Vertical") * Time.deltaTime);
playerLocation = transform.position;
GameGlobals.ref_SaveLoad.UpdateSaveData();
}
}
I have an empty game object called SaveManager that has the following script attached:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class SaveLoad : MonoBehaviour
{
private string FilePath;
public SaveData saveData;
void Awake()
{
GameGlobals.ref_SaveLoad = this;
}
void Start ()
{
FilePath = Path.Combine(Application.persistentDataPath, "save.txt");
}
void Update ()
{
// If F5 is pressed, run the Save function
if (Input.GetKeyDown(KeyCode.F5))
{
Save();
Debug.Log("save function executed");
}
// If F9 is pressed, run the Load function
if (Input.GetKeyDown(KeyCode.F9))
{
Load();
Debug.Log("load function executed");
}
}
public void Save()
{
// Debug code - see if the file exists
if (File.Exists(FilePath))
{
// If it does, delete it
File.Delete(FilePath);
}
string jsonString = JsonUtility.ToJson(saveData);
File.WriteAllText(FilePath, jsonString);
Debug.Log("Player saved to " + FilePath);
}
public void Load()
{
string jsonString = File.ReadAllText(FilePath);
JsonUtility.FromJsonOverwrite(jsonString, saveData);
UpdateLoadData();
}
public void UpdateSaveData()
{
GameGlobals.ref_SaveData.save_PlayerName = GameGlobals.ref_Player.playerName;
GameGlobals.ref_SaveData.save_PlayerLocation = GameGlobals.ref_Player.playerLocation;
GameGlobals.ref_SaveData.save_PlayerHealth = GameGlobals.ref_Player.playerHealth;
GameGlobals.ref_SaveData.save_PlayerAmmo = GameGlobals.ref_Player.playerAmmo;
}
public void UpdateLoadData()
{
GameGlobals.ref_Player.playerName = GameGlobals.ref_SaveData.save_PlayerName;
GameGlobals.ref_Player.playerLocation = GameGlobals.ref_SaveData.save_PlayerLocation;
GameGlobals.ref_Player.transform.position = GameGlobals.ref_SaveData.save_PlayerLocation;
GameGlobals.ref_Player.playerHealth = GameGlobals.ref_SaveData.save_PlayerHealth;
GameGlobals.ref_Player.playerAmmo = GameGlobals.ref_SaveData.save_PlayerAmmo;
}
}
I have an empty game object called DataManager that has the following script attached:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SaveData : MonoBehaviour
{
public string save_PlayerName;
public Vector3 save_PlayerLocation;
public float save_PlayerHealth;
public int save_PlayerAmmo;
void Awake()
{
GameGlobals.ref_SaveData = this;
}
If anyone can see what it is that I'm doing wrong, you will be saving my sanity by letting me know.
Thanks in advance.
Hello ! Have you got any problems of script ? Does $$anonymous$$onoDevelop send you a warning ? Just for knowing...
Sorry but me too i don't see where can the problem be...
You shouldn't have to manually delete the file if it exists, the WriteAllText will overwrite whatevers there. https://msdn.microsoft.com/en-us/library/system.io.file.wri$$anonymous$$lltext(v=vs.110).aspx
Also debug out the jsonString you get back after serialising. This may help narrow down whether it's a file issue or a json issue.
Okay, I debug.logged the jsonString, and in the first scen that works, the saved data appears in the console, but in the second scene that doesn't work, nothing appears at all.
In that case, check the save data that you're writing. $$anonymous$$aybe it's null. If it's null, then it may be because your save data object isn't present in the other scene. $$anonymous$$ay want to flag it as DontDestoryOnLoad, that way it will hang around when loading scenes at runtime. In the editor though you'll just have to drop another one in. Why not use your save data class as a regular serializable class that doesn't inherit from monobehaviour? It should be jsonifiable... if I've read the documentation correctly. https://docs.unity3d.com/ScriptReference/JsonUtility.ToJson.html
Benefits of this approach include: not worry about DontDestroyOnLoad and the new keyword. On top of that, you don't have to technically store the save data. If you have a reference to the player, you can dynamically build one each time you hit save. No more reference issues... Potentially. Also bare in $$anonymous$$d, if you go down the custom class route you'll be using FromJson to recreate the class for loading.
"ins$$anonymous$$d of writing saveData you write GameGlobals.ref_SaveData." Wow, I'm not sure I understand why, but that seems to make it work. I'll have to do more testing to be sure, but so far so good. Thanks so much! Could you submit that suggestion as an answer, I don't seem to have a way of converting it.
It works because GameGlobals.ref_SaveData is assigned the moment the SaveData object is created, it's assigned in the Awake function. The approach you were using was writing SaveLoad.saveData, a local variable which would need to be assigned each time in the editor, for each scene.
Okay. That makes more sense now. Thanks again.