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:
133057-save.png

In Main Menu (Load Button)

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.
OnClickButton


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;
    }
}

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.