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;
}
}
when I build my game the development console opens showing error that GenerateExp.cs script
When you build your game in what?
I build for Web. I also know that this will not work for web. Could this be the problem, saving the file to web server?
Next time please post your real code (you can still trim it but your posted code here has too many errors). Also please copy and paste the real error from the bottom console area. A lot people misinterpret the error and relay it wrong. So next time post the complete error.
Will delete the question. It seem File API is not available when building for Web.
Answer by spinnerbox · Dec 11, 2015 at 09:48 AM
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;
}
Answer by Bunny83 · Dec 10, 2015 at 12:09 PM
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...
Yes the errors you found are typos. Will repair the post.