I'm currently trying to set a "firstRun" boolean to run a piece of code only when the app is started for the first time.
GameData.cs
[System.Serializable]
public class GameData
{
public static string saveFileName = "Pixel.pixel";
public double money;
public bool firstRun;
public GameData()
{
money = GameController.Instance.CurrentCash;
}
}
SaveSystem.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class SaveSystem
{
public static void SaveData()
{
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/" + GameData.saveFileName;
FileStream stream = new FileStream(path, FileMode.Create);
GameData data = new GameData();
formatter.Serialize(stream, data);
stream.Close();
}
public static GameData LoadData()
{
string path = Application.persistentDataPath + "/" + GameData.saveFileName;
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
GameData data = formatter.Deserialize(stream) as GameData;
stream.Close();
return data;
}
else
{
return null;
}
}
}
GameController.cs part
public void Start()
{
Setup(START_CASH);
/*AddCash(START_CASH);*/
}
private void Setup(double value)
{
GameData data = SaveSystem.LoadData();
if (!data.firstRun)
{
CurrentCash += value;
SaveSystem.SaveData();
}
else
{
CurrentCash = data.money;
SaveSystem.SaveData();
}
UI.CashDisplay.text = ShortScaleString.parseDouble(CurrentCash, 1, 1000, scientificFormat);
}
My problem is that I need to check if "data.firstRun" is false/doesn't exist to run a setup part but I literally don't know how to achieve this
You should just return a new 'GameData', with your prefered value (true or false):
public static GameData LoadData()
{
string path = Application.persistentDataPath + "/" + GameData.saveFileName;
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
GameData data = formatter.Deserialize(stream) as GameData;
stream.Close();
return data;
}
else
{
return new GameData { firstRun = true };
}
}
Related
I'm trying to create a little clicker game with unity and now I need to save Shop's items in a binary file.
The below code is my Shop class which is called when I click on the button icon, I need to check if the shop data exists in my save file before loading the shop and if exists replace the default list with the saved one, example:
The first time I click on the shop the game should save every shop item available with cost, name, image and level, when the player buy something the code saves the shop items/changes into the file and when the player opens the shop again it should reload the values from the save file.
I know how to save the info into a file if is just a boolean, int, float, etc but not a list
public class Shop : MonoBehaviour
{
// Singleton Instance
public static Shop Instance = null;
[System.Serializable] public class ShopItem
{
public float costMultiplier;
public string itemName;
public Sprite image;
public float price;
public Button purchase;
public int level;
}
[SerializeField]
public List<ShopItem> shopItemsList;
GameObject ItemTemplate;
GameObject ShopItemObj;
[SerializeField] Transform ShopScrollView;
public GameObject ShopCanvas;
private void Awake()
{
SingletonSetup();
}
private void SingletonSetup()
{
if (Instance == null)
{
Instance = this;
}
else if (Instance != this)
{
Destroy(gameObject);
}
}
private void Start()
{
ItemTemplate = ShopScrollView.GetChild(0).gameObject;
int len = shopItemsList.Count;
GameData data = SaveSystem.LoadData();
for (int i = 0; i < len; i++)
{
ShopItemObj = Instantiate(ItemTemplate, ShopScrollView);
/*if (data.firstRun)
{*/
ShopItemObj.transform.GetChild(0).GetComponent<Image>().sprite = shopItemsList[i].image;
if (shopItemsList[i].level == 0)
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter(shopItemsList[i].price, GameController.Instance.scientificNumbers);
}
else
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter((shopItemsList[i].price * shopItemsList[i].costMultiplier), GameController.Instance.scientificNumbers);
}
ShopItemObj.transform.GetChild(2).GetComponent<TextMeshProUGUI>().text = shopItemsList[i].itemName;
float price = shopItemsList[i].price;
ShopItemObj.transform.GetChild(3).GetComponent<Button>().onClick.AddListener(() => { OnPuchase(price); });
ShopItemObj.transform.GetChild(4).GetComponent<TextMeshProUGUI>().text = "level: " + shopItemsList[i].level;
/*}
else
{
ShopItemObj.transform.GetChild(0).GetComponent<Image>().sprite = data.shopItemsList[i].image;
if (shopItemsList[i].level == 0)
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter(data.shopItemsList[i].price, GameController.Instance.scientificNumbers);
}
else
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter((data.shopItemsList[i].price * data.shopItemsList[i].costMultiplier), GameController.Instance.scientificNumbers);
}
ShopItemObj.transform.GetChild(2).GetComponent<TextMeshProUGUI>().text = data.shopItemsList[i].itemName;
float price = data.shopItemsList[i].price;
ShopItemObj.transform.GetChild(3).GetComponent<Button>().onClick.AddListener(() => { OnPuchase(price); });
ShopItemObj.transform.GetChild(4).GetComponent<TextMeshProUGUI>().text = "level: " + data.shopItemsList[i].level;
}*/
}
Destroy(ItemTemplate);
}
private void Update()
{
int len = shopItemsList.Count;
for (int i = 0; i < len; i++)
{
if (!GameController.Instance.CanAffordPurchase(shopItemsList[i].price))
{
ShopItemObj.transform.GetChild(3).GetComponent<Button>().enabled = false;
}
else
{
ShopItemObj.transform.GetChild(3).GetComponent<Button>().enabled = true;
}
}
}
public void OnPuchase(float price)
{
if (GameController.Instance.CanAffordPurchase(price))
{
int len = shopItemsList.Count;
for (int i = 0; i < len; i++)
{
if (shopItemsList[i].level < 1)
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter(shopItemsList[i].price, GameController.Instance.scientificNumbers);
GameController.Instance.PurchaseItem(price);
}
else
{
ShopItemObj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = "$" + GameController.Instance.MoneyConverter(shopItemsList[i].price * shopItemsList[i].costMultiplier, GameController.Instance.scientificNumbers);
shopItemsList[i].price = shopItemsList[i].price * shopItemsList[i].costMultiplier;
GameController.Instance.PurchaseItem(price * shopItemsList[i].costMultiplier);
}
ShopItemObj.transform.GetChild(4).GetComponent<TextMeshProUGUI>().text = "level: " + (shopItemsList[i].level += 1);
}
SaveSystem.SaveData();
}
else
{
Debug.LogError("Cannot affor the item");
}
}
public void HideCanvas()
{
ShopCanvas.gameObject.SetActive(false);
}
public void ShowCanvas()
{
ShopCanvas.gameObject.SetActive(true);
}
}
My GameData.cs:
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[Serializable]
public class GameData
{
public static string saveFileName = "Clicker.unity2d";
public float money;
public bool firstRun;
public bool scientificNumbers;
public List<Shop.ShopItem> shopItemsList;
public GameData()
{
money = GameController.Instance.money;
firstRun = GameController.Instance.firstRun;
scientificNumbers = GameController.Instance.scientificNumbers;
}
}
public static class SaveSystem
{
public static void SaveData()
{
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/" + GameData.saveFileName;
FileStream stream = new FileStream(path, FileMode.Create);
GameData data = new GameData();
formatter.Serialize(stream, data);
stream.Close();
}
public static GameData LoadData()
{
string path = Application.persistentDataPath + "/" + GameData.saveFileName;
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
GameData data = formatter.Deserialize(stream) as GameData;
stream.Close();
return data;
}
else
{
return new GameData { firstRun = true };
}
}
}
Where should the list be saved? The list should be in an object (for example your GameData has also List<>), so you can use the function below to create a file and save your List using BinaryFormatter:
private void CreateGameFile(List<Shop.ShopItem> list)
{
string path = Path.Combine(Application.persistentDataPath + "/" + gameName + GetGameFileExtension());
IFormatter formatter = new BinaryFormatter();
/// Create game file.
FileStream file = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite);
GameData game = new GameData
{
money = GameController.Instance.money,
firstRun = GameController.Instance.firstRun,
scientificNumbers = GameController.Instance.scientificNumbers,
shopItemsList = list
};
formatter.Serialize(file, game);
file.Close();
}
I'm using binaryFormatter to encrypt data but i wanna save it without encryption.
so next time i just cast it .
this is the code that i'm using.
//Creat a binaryFormatter
BinaryFormatter formatter = new BinaryFormatter();
//Direction, Filename and Extention
string path = Application.persistentDataPath + "/enem.sav";
//Creat the File (Blank)
FileStream stream = new FileStream(path, FileMode.Create);
//Get the Data
EnemData data = new EnemData();
//Enter the Data and Encrypt it
formatter.Serialize(stream, data);
stream.Close();
You can use JsonUtility.ToJson to convert your object's data into JSON format.
public class PlayerState : MonoBehaviour
{
public string playerName;
public int lives;
public float health;
public string SaveToString()
{
return JsonUtility.ToJson(this);
}
// Given:
// playerName = "Dr Charles"
// lives = 3
// health = 0.8f
// SaveToString returns:
// {"playerName":"Dr Charles","lives":3,"health":0.8}
}
Here you can find how to read and write a string to and from a file.
// Create a file to write to.
string createText = "Hello and Welcome" + Environment.NewLine;
File.WriteAllText(path, createText);
// Open the file to read from.
string readText = File.ReadAllText(path);
To load the data simply use JsonUtility.FromJson or JsonUtility.FromJsonOverwrite.
Example:
public class PlayerState : MonoBehaviour
{
public string playerName;
public int lives;
public float health;
public string path = "your file path";
public string SaveToString()
{
File.WriteAllText(path, JsonUtility.ToJson(this));
}
public static PlayerState Load(string path)
{
return JsonUtility.FromJson<PlayerState>(File.ReadAllText(path));
}
}
Not sure about what you need. If you just want to serialize (turn your class into a binary representation and save to a file) and then get it back this should work for your:
//Create a binaryFormatter
BinaryFormatter formatter = new BinaryFormatter();
//Direction, Filename and Extention
string path = Application.persistentDataPath + "/enem.sav";
//Creat the File (Blank)
Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
//Get the Data
EnemData data = new EnemData { Id = 123, SomeBool = true, Name = "Enem" };
//Enter the Data and Encrypt it
formatter.Serialize(stream, data);
stream.Close();
//Restore it
Stream stream2 = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
EnemData restoredClass = (EnemData)formatter.Deserialize(stream2);
stream.Close();
But remember that you have to mark your class as serializable:
[Serializable]
public class EnemData
{
public int Id { get; set; }
public bool SomeBool { get; set; }
public string Name { get; set; }
}
So I'm saving and loading values from a specific class. I'm calling out the class that holds the values and using them. But for some reason when the values are loaded in the class that holds the values, the class that calls out the class with values doesn't get those values. How can resolve this?
Checked if values are loaded in, they are(shown in SaveSystem class)
Checked if values are received in the Shop class, they aren't(shown)
public class AllData {
public double bucketProg;
public double milk;
public double totalMilk;
public double milkPrice;
public double totalClicks;
public double coins;
public double totalCoins;
public double upgradeBucketCost = 10;
public double qualityMilkCost = 1000;
public double BucketLevel = 1;
public double milkLevel = 1;
public double multiplier = 1;
}
public class SaveSystem {
AllData allData = new AllData();
public void Save(AllData allData) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = new FileStream(Application.persistentDataPath + "/allData.hey", FileMode.Create);
bf.Serialize(file, allData);
file.Close();
}
public void Load() {
if (File.Exists(Application.persistentDataPath + "/allData.hey")) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/allData.hey", FileMode.Open);
allData = (AllData) bf.Deserialize(file);
file.Close();
//here I made sure to check if the data is being loaded, which it did
Debug.Log(allData.milk);
}
}
}
public class Shop: MonoBehaviour {
AllData allData = new AllData();
private SaveSystem saveSystem = new SaveSystem();
public Text milkText;
public void Start() {
saveSystem.Load();
}
public void Update() {
//output is 0
milkText.text = allData.milk;
saveSystem.Save(this.allData);
}
}
Seems like the problems is you forgetting to mark AllData as System.Serializable;
Also, making the Load function return the AllData that was loaded.
After some modifications, I tested it on my end and it seems to work.
(Tested it on Unity 2019.1.12f1)
This was the code that I used to test:
Shop.cs
public class Shop : MonoBehaviour {
AllData allData = new AllData();
private SaveSystem saveSystem = new SaveSystem();
public Text milkText;
public void Start() {
// assigns '-1' if data was not found;
var loadedData = saveSystem.Load();
allData.milk = (int) (loadedData == null ? -1 : loadedData.milk);
milkText.text = allData.milk.ToString();
}
public void Update() {
if (Input.GetKeyDown(KeyCode.K)){
TestSaving();
}
if (Input.GetKeyDown(KeyCode.J)) {
TestAddingMilkValue();
}
if (Input.GetKeyDown(KeyCode.I)) {
TestLoadingMilkValue();
}
#region Local_Function
void TestSaving() {
Debug.Log("SAVING...");
saveSystem.Save(this.allData);
}
void TestAddingMilkValue() {
Debug.Log("ADD...");
++allData.milk;
milkText.text = allData.milk.ToString();
}
void TestLoadingMilkValue() {
Debug.Log("LOADING...");
var loadedData = saveSystem.Load();
allData.milk = (int)(loadedData == null ? -1 : loadedData.milk);
milkText.text = allData.milk.ToString();
}
#endregion
}
}
SaveSystem.cs
[System.Serializable]
public class AllData {
public double bucketProg = 0;
public double milk = 0;
public double totalMilk = 0;
public double milkPrice = 0;
public double totalClicks = 0;
public double coins = 0;
public double totalCoins = 0;
public double upgradeBucketCost = 10;
public double qualityMilkCost = 1000;
public double BucketLevel = 1;
public double milkLevel = 1;
public double multiplier = 1;
}
public class SaveSystem {
AllData allData = new AllData();
public void Save(AllData allData) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = new FileStream(Application.persistentDataPath + "/allData.hey", FileMode.Create);
bf.Serialize(file, allData);
file.Close();
}
public AllData Load() {
if (File.Exists(Application.persistentDataPath + "/allData.hey")) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/allData.hey", FileMode.Open);
allData = (AllData)bf.Deserialize(file);
file.Close();
return allData;
}
return null;
}
(^ Might want to separate AllData into a different source file.)
Though it is just my personal preference, but you can refactor Load into this too:
// Returns true if the data exists; False if the data did not.
public bool TryLoad(out AllData allData) {
allData = null;
if (File.Exists(Application.persistentDataPath + "/allData.hey")) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/allData.hey", FileMode.Open);
allData = (AllData)bf.Deserialize(file);
file.Close();
}
return allData != null;
}
This way, you don't have to manually check if AllData is null whenever you fetch it, as the function "auto-checks" for you.
So, I'm currently using C# in unity to create a save file in any operational system
using the "Application.persistentDataPath" command.
on windows 10 it's supposed to create this file in
C:\Users\User\AppData\LocalLow
thing is, it isn't creating the file.
while the application is open, the data is saved and stored, when I load, everything saved comes up
but when the application is closed and open again, the previous saved data simply doesn't exists
I'm just trying to make it create a file, and save data in it
I have tried several videos, several personal courses and I followed each and every single one of them
so far, the save function worked, but as described, no save file is created
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Collections.Generic;
public static class SaveSystem
{
public static BeerPiratePS beerpirateps { get; private set; }
public static BeerPS beerps { get; private set; }
public static BeerPubPS beerpubps { get; private set; }
public static FactoryPS factoryps { get; private set; }
public static GlobalBeer globalbeer { get; private set; }
public static BeerChemistsPS beerchem { get; private set; }
public static MainButtonClick mainbuttonclick { get; private set; }
public static void SavePlayer ()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = File.Create(Application.persistentDataPath + "/beerplayer.ber");
Debug.Log("BEER SAVED AAAAAAAAAAAAAAAAAAA");
PlayerData dataPub = new PlayerData(beerpubps);
PlayerData dataDwarves = new PlayerData(factoryps);
PlayerData dataPirates = new PlayerData(beerpirateps);
PlayerData dataBrew = new PlayerData(beerps);
PlayerData dataGlobal = new PlayerData(globalbeer);
PlayerData dataChemist = new PlayerData(beerchem);
PlayerData dataClick = new PlayerData(mainbuttonclick);
formatter.Serialize(stream, dataPub);
formatter.Serialize(stream, dataPirates);
formatter.Serialize(stream, dataDwarves);
formatter.Serialize(stream, dataBrew);
formatter.Serialize(stream, dataGlobal);
formatter.Serialize(stream, dataClick);
formatter.Serialize(stream, dataChemist);
stream.Close();
}
public static PlayerData LoadPlayer()
{
string path = Application.persistentDataPath + "/beerplayer.ber";
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = File.Open(Application.persistentDataPath + "/beerplayer.ber", FileMode.Open);
Debug.Log("BEER FOUND AAAAAAAAAAAAAAAAAAAAAAAAAAA");
PlayerData dataPub = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataPirates = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataDwarves = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataBrew = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataGlobal = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataClick = (PlayerData)formatter.Deserialize(stream) as PlayerData;
PlayerData dataChemist = (PlayerData)formatter.Deserialize(stream) as PlayerData;
stream.Close();
return dataChemist;
}
else
{
Debug.LogError("NO BEER FOUND AAAAAAAAAAAAAAAAAAAAA" + path);
return null;
}
}
}
surprisingly, the logError "NO BEER FOUND AAAAAAAAAAAAAAAAAA" hasn't appeared once
Is there a way to save a list to disk generically? I tried data contract serializer, but it always generates an empty list.
public static List<T> Load<T>() where T : class,new()
{
var serializer = new DataContractSerializer(typeof(List<T>));
string path = HttpContext.Current.Server.MapPath("~/App_Data/" + typeof(T).ToString() + ".xml");
if (!System.IO.File.Exists(path))
{
return new List<T>();
}
else
{
using (var s = new System.IO.FileStream(path, System.IO.FileMode.Open))
{
return serializer.ReadObject(s) as List<T>;
}
}
}
public static void Save<T>(List<T> data) where T : class,new()
{
var serializer = new DataContractSerializer(typeof(List<T>));
Enumerate<T>(data);
string path = HttpContext.Current.Server.MapPath("~/App_Data/" + typeof(T).ToString() + ".xml");
using (var s = new System.IO.FileStream(path, System.IO.FileMode.Create))
{
serializer.WriteObject(s, data);
}
}
You might want to use JavaScriptSerializer
var json = new JavaScriptSerializer().Serialize(thing);
JSON lists are generic.
UPDATE: Ass claimed by TimS Json.NET is better serializer so if adding 3rd party library is an option here is an article on how to do it.
What about a binary serializer
public static void SerializeToBin(object obj, string filename)
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
bf.Serialize(fs, obj);
}
}
public static T DeSerializeFromBin<T>(string filename) where T : new()
{
if (File.Exists(filename))
{
T ret = new T();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
ret = (T)bf.Deserialize(fs);
}
return ret;
}
else
throw new FileNotFoundException(string.Format("file {0} does not exist", filename));
}
Based on what you're trying to do, the key might also be your class T, ensuring that it is decorated with the proper [DataContract] and [DataMember] attributes. Here is a working example based on your code in question (however if you don't care to be able to utilize the persisted file outside of your code, you might find better performance in the binary serializer):
[DataContract]
public class Mydata
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Mydata> myData = new List<Mydata>();
myData.Add(new Mydata() { Id = 1, Name = "Object 1" });
myData.Add(new Mydata() { Id = 2, Name = "Object 2" });
string path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + #"\" + typeof(Mydata).ToString() + ".xml";
Save<Mydata>(myData, path);
List<Mydata> myNewData = Load<Mydata>(path);
Console.WriteLine(myNewData.Count);
Console.ReadLine();
}
public static List<T> Load<T>(string filename) where T : class
{
var serializer = new DataContractSerializer(typeof(List<T>));
if (!System.IO.File.Exists(filename))
{
return new List<T>();
}
else
{
using (var s = new System.IO.FileStream(filename, System.IO.FileMode.Open))
{
return serializer.ReadObject(s) as List<T>;
}
}
}
public static void Save<T>(List<T> list, string filename)
{
var serializer = new DataContractSerializer(typeof(List<T>));
using (FileStream fs = new FileStream(filename, FileMode.Create))
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs))
{
serializer.WriteObject(writer, list);
}
}
}
}