I am a beginner at coding and using Unity. There is so much to learn!
I’m trying to make a game as I learn, so far I made the game and it worked when I saved data using PlayerPrefs.
However I want to use the game at schools where I teach English in Japan, so I wanted to make it possible for the kids to login and retrieve their data from a previous time playing the game.
I have a log in scene, where the player must enter a name or number, then the data loads and the main menu opens. However when I try to access the serialized data in the Main Menu I get
“NullReferenceException: Object reference not set to an instance of an object
MainMenu.Start () (at Assets/Scripts/MainMenu.cs:23)”
I have an empty object in the log in scene which is not Destroyed and remains in the Hierarchy in the MainMenu, it has the script attached to the object.
I have attached the relevant scripts, PlayerData, LogInStart, MainMenu and the serialized data script PlayerProgress.
I would really appreciate any advice or help you can give me to solve this problem.
It’s much better if you post the scripts in a post with Code tags , though we really only need to see line 23 in mainMenu to get you going in the right direction.,
Many Thanks for the replies. Here is one of the scripts as suggested.
Thank you for link explaining how to add the code.
Please explain how should I initiate the Player Progress inside the PlayerData singleton.
Although the error happens in the MainMenu script it happens when I first try to get the data using
PlayerData.Instance.PlayerProgress.Currency.
So I assume if I only need to correct the error in this script?
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class PlayerData : MonoBehaviour {
static PlayerData _instance;
//Access the Load () methods and Save() through this property
public static PlayerData Instance {
get {
if (_instance == null)
_instance = FindObjectOfType<PlayerData> ();
if (_instance == null) {
GameObject go = new GameObject ("_PlayerData");
DontDestroyOnLoad (go);//This keeps the PlayerSave object in memory throughout the game
_instance = go.AddComponent<PlayerData> ();
}
return _instance;
}
}
public PlayerProgress PlayerProgress{ get; private set; }
public string CurrentPlayer { get; private set; }
public PlayerProgress Load(string username)
{
//Get the path to the player7s save game
string path = GetPlayerFilePath (username);
//Store the current player for when we have to save the progress later
CurrentPlayer = username;
//Does the player progress file exist?
if (File.Exists (path)) {
//Set the public property PlayerProgress of this object to the contents of the JSON
//file
string json = File.ReadAllText (path);
PlayerProgress = JsonUtility.FromJson<PlayerProgress> (json);
} else {
//File didn't exist , let's create a blank PlayerProgress
PlayerProgress = new PlayerProgress ();
PlayerProgress.CurrentSkinIndex = 0;
PlayerProgress.Currency = 0;
PlayerProgress.SkinAvailability = 1;
PlayerProgress.SeeColour = 0;
PlayerProgress.ListenColour= 0;
PlayerProgress.ReadColour = 0;
PlayerProgress.SeeFruit = 0;
PlayerProgress.ListenFruit = 0;
PlayerProgress.ReadFruit = 0;
PlayerProgress.ColourClose = 1;
PlayerProgress.FruitClose = 1;
}
return PlayerProgress;
}
public void Save(string username)
{
//if the username parameter isnt specified , use the current user
if (string.IsNullOrEmpty(username))
{
username = CurrentPlayer;
}
//if the username isnt blank we have a valid user
if (!string.IsNullOrEmpty(username))
{
//Get the JSON string
string json = JsonUtility.ToJson(PlayerProgress);
//Get the path
string path = GetPlayerFilePath(username);
//Remove a previous version of the file
if (File.Exists(path))
{
File.Delete (path);
}
//Write the Json out to the file.
File.WriteAllText(path,json);
}
}
//Helper method to get the progress file for the player
string GetPlayerFilePath(string username)
{
string path = Path.Combine (Application.persistentDataPath, username + "_progress.txt");
return path;
}
}
Yeah, your PlayerProgress is never set, because it’s never loaded.
This looks like it’s Frankensteined a bit from some tutorials. Save assumes that the player’s progress is stored in the PlayerProgress property. But the setter is never used, so Save won’t do anything! The PlayerData and PlayerProgress are kinda half-separate. There’s also a bunch of different possible Players, since you’re loading PlayerProgress through UserName, but PlayerData is a singleton, meaning there’s only one of it.
If you change Load to be a void method that assigns the loaded player to the PlayerProgress property, you should be good if you always enter through logInStart. It’s still a bit of a cumbersome design.
Yeah I’m learning from various books and tutorials, so I decided to make a game while I learn.
I want to continue making games so the next game will have a much smoother design code from the beginning.
I thought I was already Loading the PlayerProgress in line 26 of my LogInStart script, is that not the case?
If the file does not exist it gives the default values in PlayerData from lines 44 to 60.
I’m confused how I can change the Load to assign the loaded player to the PlayerProgress please explain.
The Player Prefs was easy for me to understand(simply set and get), but it limited the game to one set of data. At school there could be as many as 20 kids using one computer so they needed the sign in, serializing seems to be the way but I just cant fully understand how to load and save the data.
Please help…
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LogInStart : MonoBehaviour {
public GameObject PlayButton;
public InputField nameField;
public string playername;
public Text PlayerName;
// Use this for initialization
void Awake () {
PlayButton.SetActive(false);
}
public void OnEnter()
{
playername = nameField.text;
PlayerName.text = nameField.text;
if (playername == "")
{ } else
{
PlayerData.Instance.Load(playername);
PlayButton.SetActive(true);
}
}
public void MainMenu()
{
SceneManager.LoadScene("MainMenu");
}
}
I missed that you were assigning to PlayerProgress in your Load code. Got confused by the return of the method. Why’s it returning? Anyway, yeah, that’s not the problem.
Could it be that you have a PlayerData in the scene with LogInStart in it, but not in the scene with Main Menu in it? In that case, the Instance would be the object found with FindObjectOfType<> in the LogInStart scene, which isn’t set to DontDestroyOnLoad, so it’ll be deleted when you leave the scene.
In that case, remove the object from your LogInStart scene, and remove the FindObjectOfType-fetching of the instance.
Many thanks for your last reply. It set me thinking about the empty Object I had with the PlayerData script attached to it.
I had that empty Object in the LogInScene, thinking it should be in the first scene and it would be okay because it was Don’tDestroyOnLoad. However, I wondered if it was there too early for the script call, so I deleted it and added it to the MainMenu scene.
It’s working perfectly, I can log in with different names and create different data and retrieve the data.
Thank you very much for your replies, no doubt I will use the forum again in the future there is so much to learn and so many mistakes to make too.