- Home /
Save Load Unity JSON
Hey guys. I have a problem in loading a data from json. My save button is working so far, but my load button its not properly functioning. What I did is, my save button can be see on in-game while the load button is on the main menu. So in the in-game, I can save the scene name and current position of my character, but when I click the load button on the menu, the scene will open what's on the json. For example the sceneName is "StartQuest" then it can load it. My problem is how can I load the position and rotation of my character base on the json? Here's what I did:
public class GameSaving {
public string sceneName;
public float[] position;
public float[] rotation;
}
**SaveLoadManager.cs**
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class SaveLoadManager : MonoBehaviour {
public static SaveLoadManager saveLoadManager;
private string jsonSavePath;
public GameSaving gameSaving;
[SerializeField] private Player player;
[SerializeField] private Button saveButton;
[SerializeField] private Button loadButton;
private string savedSceneName;
void Awake() {
if (saveLoadManager == null) {
saveLoadManager = this;
} else if (saveLoadManager != this) {
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject);
}
void OnEnable() {
gameSaving = new GameSaving ();
saveButton.onClick.AddListener(delegate { OnSaveButtonClick(); });
loadButton.onClick.AddListener (delegate { OnLoadButtonClick(); });
//LoadData ();
}
void Start() {
jsonSavePath = Application.persistentDataPath + "/saveload.json";
}
public void OnSaveButtonClick() {
SaveData();
Debug.Log ("Saved");
}
public void OnLoadButtonClick() {
LoadData ();
Debug.Log ("Loaded");
}
private void SaveData() {
//References
Scene scene = SceneManager.GetActiveScene ();
//Scene Name
gameSaving.sceneName = scene.name;
//Position
gameSaving.position = new float[3];
gameSaving.position [0] = player.transform.position.x;
gameSaving.position [1] = player.transform.position.y;
gameSaving.position [2] = player.transform.position.z;
//Rotation
gameSaving.rotation = new float[3];
gameSaving.rotation [0] = player.transform.rotation.x;
gameSaving.rotation [1] = player.transform.rotation.y;
gameSaving.rotation [2] = player.transform.rotation.z;
string jsonData = JsonUtility.ToJson (gameSaving, true);
File.WriteAllText (jsonSavePath, jsonData);
}
public void LoadData() {
gameSaving = JsonUtility.FromJson<GameSaving>(File.ReadAllText(Application.persistentDataPath + "/saveload.json"));
savedSceneName = gameSaving.sceneName;
Vector3 position;
position.x = gameSaving.position [0];
position.y = gameSaving.position [1];
position.z = gameSaving.position [2];
transform.position = new Vector3(position.x, position.y, position.z);
Quaternion rotation;
rotation.x = gameSaving.rotation [0];
rotation.y = gameSaving.rotation [1];
rotation.z = gameSaving.rotation [2];
transform.rotation = Quaternion.Euler(rotation.x, rotation.y, rotation.z);
SceneManager.LoadScene (savedSceneName);
//For testing purposes
Debug.Log (transform.position);
Debug.Log (transform.rotation);
Debug.Log (savedSceneName);
}
}
So I created an empty object on StartQuest scene and Main Menu Load. This what I did and I got these error when I start the MainMenu. Object reference not set to an instance of an object SaveLoadManager.OnEnable () (at Assets/Scripts/SaveGame/SaveLoadManager.cs:33)
In Start Quest:
In Main Menu (Load Button)
Answer by bpaynom · Feb 13, 2019 at 08:50 AM
void OnEnable() {
gameSaving = new GameSaving ();
if ( saveButton != null )
saveButton.onClick.AddListener(delegate { OnSaveButtonClick(); });
if ( loadButton != null )
loadButton.onClick.AddListener (delegate { OnLoadButtonClick(); });
//LoadData ();
}
BTW, why do you add listeners that way instead of doing it in the inspector (Button-> OnClick) so you don't have to write references and you can just call the function you want on each button.
Also, I would change your GameSaving class , and other minor changes, for looking like this:
public class GameSaving
{
public string sceneName; // This will be written in the json file
public Vector3 serializedPosition; // This will be written in the json file
public Quaternion serializedRotation; // This will be written too.
public static Vector3 position; // Static fields doesnt write into json
public static Quaternion rotation; // Static fields doesnt write into json
public static bool loaded = false;
}
public static void LoadData()
{
GameSaving gameSaving = JsonUtility.FromJson<GameSaving>( File.ReadAllText( Application.persistentDataPath + "/saveload.json" ) );
GameSaving.position = gameSaving.serializedPosition;
GameSaving.rotation = gameSaving.serializedRotation;
SceneManager.LoadScene( gameSaving.sceneName );
//For testing purposes
Debug.Log( transform.position );
Debug.Log( transform.rotation );
Debug.Log( gameSaving.sceneName );
GameSaving.loaded = true;
}
public static void SaveData(Player player)
{
//References
Scene scene = SceneManager.GetActiveScene();
GameSaving gameSaving = new GameSaving();
//Scene Name
gameSaving.sceneName = scene.name;
//Position
gameSaving.serializedPosition = player.transform.position;
GameSaving.position = gameSaving.serializedPosition;
//Rotation
gameSaving.serializedRotation = player.transform.rotation;
GameSaving.rotation = gameSaving.serializedRotation;
string jsonData = JsonUtility.ToJson( gameSaving , true );
File.WriteAllText( jsonSavePath , jsonData );
}
// In your player script
private void Start()
{
if ( GameSaving.loaded ){
transform.position = GameSaving.position;
transform.rotation = GameSaving.rotation;
GameSaving.loaded = false;
}
}
How can I load the character's position base on the json data?
Your functions should do it. You just add to the onclick button (throgh inspector) the object and function you want to call. So add your player object to object slot, then add the function loadData to the function slot.
The problem here is that you're changing the scene, so the player will be destroyed unless it has DoNotDestroyOnLoad. So, one way could be to add static Vector3 startPosition to your player script, store it on the LoadData function, and the retrieve it in Start() function on your player script.
Thanks, its working now on loading the scene. How can I store it on the LoadData function and retrieve on Start method? Thanks.
Answer by misher · Feb 13, 2019 at 09:26 AM
Your SaveLoadManager can be completely a static class or a scriptable object (so you can reference it from any monobehaviour), and it should not really contain any state, just 2 methods (load and save).
Then there can be a bunch of other scripts who use this SaveLoadManager, for example, UI scripts for the buttons.
Separate Loading and parsing the JSON with the scene management, you want to load your saved game then, maybe display a message with a button to continue the game. This behavior can be implemented in say "sturtup manager".
To pass the loaded state to another scene you can create a separate "state" container, it can be a singleton, or any other method, if you use singleton remember to create it only once in your "startup" scene (you always should have a sturtup scene in your unity project).
Basically, split the functionality into different pieces so you can effectively debug them and change.