Using singleton pattern - cannot access private/protected method which is public

I made a GameState class in C# that serves as a save/load game data tool. I used singleton pattern as explained in this video: https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/persistence-data-saving-loading

All works in play mode inside unity (Unity 5.1.4) and I don’t get any errors. However when I build my game the development console opens showing error that GenerateExp.cs script cannot access private/protected method from GameState.cs script. The method is called Load() and it is btw

public void Load();

not private or protected as the error says. What could be possibly going on?
I thought maybe it could be initialization problem so I placed the GameState game object which holds GameState.cs script component at top of my scene list.

Here are some code samples:

GameState.cs

public Class GameState : MonoBehaviour 
{
      public static GameState gameState;
      public float experience;
      public float score;

      void Awake () 
  {
	 if (gameState == null) 
	{
		DontDestroyOnLoad(gameObject);
		gameState = this;
	}
	else if (gameState != this) 
	{
		Destroy(gameObject);
	}
  }
      public void Load()
      {
         // specifically it complains about File.Exists part
         if(File.Exists(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME))
         {
               BinaryFormatter bf = new BinaryFormatter();
           FileStream fs = File.Open(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME, FileMode.Open);
	GameData data = (GameData)bf.Deserialize(fs);
			
	experience = data.experience;
	score = data.score;

            fs.Close();
               
         }
         // check if file exists, then load data from file on disk. 
         //If not create new file with default data
       }

      public void Save()
     {
        // save game data to file on disk
     }
}
// in the same file GameState.cs
    [Serializable]
    class GameData 
    {
	public float experience = 0;
	public float score = 0;
    }

GenerateExp.cs

public class GenerateExp: MonoBehaviour
{
       public float experience;
       public float score;
      void Awake ()
      {
          GameState.gameState.Load(); // this is the line where the code break in the build
		
           experience = GameState.gameState.experience;
           score = GameState.gameState.score;
       }
}

Your example code doesn’t seem to be your real code. First you have written “Class” with a capital letter. This wouldn’t compile at all.

Second you have two classes inside your “GameState.cs” file:

  • The public class “GameState”
  • The private class “GameData”

Inside your “GenerateExp” class you use

GameData.gameState.Load();

which doesn’t make any sense as the GameData class is private and it doesn’t contain a static “gameState”.

Likewise the following two lines are similar:

experience = GameState.gameState.experience;
score = GameState.gameState.score;

Here you correctly access the singleton instance of your GameState class, but then you try to access a “experience” and “score” variable which doesn’t exist in your GameState class.

You should do the following:

  • The GameData class should be public
  • You probably want to add a public variable of type GameData to your GameState class. Lets call it “data”.
  • When you want to call “load” you have to use GameState.gameState.Load();
  • To access the “experience” variable you have to use GameState.gameState.data.experience

To sum up:

[Serializable]
public class GameData 
{
    public float experience = 0;
    public float score = 0;
}

public Class GameState : MonoBehaviour 
{
    public static GameState gameState;
    public GameData data;
    // ....

And the usage would be something like:

GameState.gameState.Load();
experience = GameState.gameState.data.experience;
score = GameState.gameState.data.score;

Note: Your code won’t compile, no matter if you build your game or not. If that’s your real code must have compiler errors in Unity.

edit
Just saw your edit and comment above. You can’t use any method that uses files on the local machine when you build for web (WebGL or Webplayer). Just forget about the “File” class. It can’t be used. If you have a server you have to use some serverside script which you can reach using the WWW class.

See the example for ServerSide Highscores on the wiki.

However when you try using the File class you should als get compiler errors. You really should check your console…

So if you publish to Web, use PlayerPrefs class. It has the same purpose as SharedObject had for Flash/AS3 in Web. 1MB of data available, for strings, ints, floats = HTML5 storage.

However, if you publish to other platforms use, BinaryFormatter and FIleStream to serialize or deserialize data to local file on disk.

Here is my code:

using UnityEngine;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

public class SaveLoadGameData : MonoBehaviour 
{
	public static SaveLoadGameData gameState;
	
	public float experience = 0;
	public float score = 0;
	
	void Awake () 
	{
		if (gameState == null) 
		{
			DontDestroyOnLoad(gameObject);
			gameState = this;
		}
		else if (gameState != this) 
		{
			Destroy(gameObject);
		}
	}
	
	public void Save ()
	{
		switch(Helper.BUILD_TYPE)
		{
			case Helper.BUILD_FOR_WEB:
				PlayerPrefs.SetFloat(Helper.EXP_KEY, experience);
				PlayerPrefs.SetFloat(Helper.SCORE_KEY, score);
				break;
				
			case Helper.BUILD_FOR_WIN_X86:
				BinaryFormatter bf = new BinaryFormatter();
				FileStream fs = File.Create(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME);
				
				GameData data = new GameData();
				data.experience = experience;
				data.score = score;
				
				bf.Serialize(fs, data);
				fs.Close();
				break;
				
			default:
				Debug.Log("Save method: " + Helper.WRONG_BUILD_TYPE_SELECTED_ERR);
				break;
		}
	}
	
	public void Load ()
	{
		switch(Helper.BUILD_TYPE)
		{
			case Helper.BUILD_FOR_WEB:
				experience = PlayerPrefs.GetFloat(Helper.EXP_KEY, 0);
				score = PlayerPrefs.GetFloat(Helper.SCORE_KEY, 0);
				break;
				
			case Helper.BUILD_FOR_WIN_X86:
				if (File.Exists(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME))
				{
					try
					{
						BinaryFormatter bf = new BinaryFormatter();
						FileStream fs = File.Open(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME, FileMode.Open);
						GameData data = (GameData)bf.Deserialize(fs);
						
						experience = data.experience;
						score = data.score;
						
						fs.Close();
					} 
					catch (Exception e)
					{
						Debug.Log(e.Message);
					}
				}
				else
				{
					// TODO
					Save();
				}
				break;

			default:
				Debug.Log("Load method: " + Helper.WRONG_BUILD_TYPE_SELECTED_ERR);
				break;
		}
	}
	
	public void ResetGameState () 
	{
		experience = 0;
		score = 0;
		
		Save();
	}
}

[Serializable]
class GameData
{
	public float experience = 0;
	public float score = 0;
}