I was building an application and wanted to get data through text file. when i put the location of any folder of computer then it is working properly but when i changed the location and use that app with android smart phone it doesn't save txt file in mobile's internal storage or sdcard.
How can i save .txt file in mobile internal storage?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
public class click2 : MonoBehaviour
{
public static bool mobileSD;
public static string saveLocation;
public void onclick()
{
// in order to save at sdcard, make it 'true'
mobileSD = true;
if (mobileSD == false) { saveLocation = "d:/"; }
else { saveLocation = "/sdcard/Download/"; }
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#saveLocation + 1 + ".txt", true))
{
file.Write("a");
file.Close();
}
}
}
}
Save Data in your Application.persistentDataPath
https://unity3d.com/learn/tutorials/topics/scripting/persistence-saving-and-loading-data
First :
Write Your Data when you start the app.
Second: when you want to load your Data
Check if the File Existed in the Application.persistentDataPath
and if true you can read it normally, if not write your default settings in It.
Try This code and Customize it depending on Your Requirements :
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class SaveData : MonoBehaviour
{
// Start is called before the first frame update
public string DATA_PATH = "player.json";
void Start()
{
GamePalyer p = new GamePalyer();
p.Id = 1;
p.Name="John Smith";
p.Score = 100;
Debug.Log("Starts Saving");
SaveDataToFile(p);
ReadData();
}
public void SaveDataToFile(GamePalyer _player)
{
string filePath = Application.persistentDataPath + "/" + DATA_PATH;
Debug.Log("Saving At : "+ filePath);
string savedData = JsonUtility.ToJson(_player);
File.WriteAllText(filePath, savedData);
}
public void ReadData()
{
string filePath = Application.persistentDataPath + "/" + DATA_PATH;
if (File.Exists(filePath))
{
string data = File.ReadAllText(filePath); ;
GamePalyer player = JsonUtility.FromJson<GamePalyer>(data);
Debug.Log(player.Id);
Debug.Log(player.Name);
Debug.Log(player.Score);
}
}
}
[System.Serializable]
public class GamePalyer
{
public int Id;
public string Name;
public float Score;
}
Related
I am making a simple memory game. I have already made the game work with smartfoxserver. But when I tried to build another machine and let them run simultaneously, one player would be log out when another log in. Could you guys help me with this one. Here is the code on the client. Also is once the game start is there any way for the two machine to connect to eachother. For example showing the score from Player1 to Player2. Thank you.
using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities.Data;
using Sfs2X.Requests;
using Sfs2X.Util;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using Sfs2X.Requests.MMO;
public class GameController : MonoBehaviour
{
public string defaultHost = "127.0.0.1";
public int defaultTcpport = 8888;
public int defaultWsport = 8080;
public string Zonename = "BasicExamples";
public string Username = "guest";
public string Roomname = "The Lobby";
private SmartFox sfs;
void Awake()
{
SourceSprites = Resources.LoadAll<Sprite>("Sprite/GameImages");
}
void Start()
{
Login_Click();
TotalGuess = btnlist.Count / 2;
GetButton();
AddListener();
AddSprites();
shuffle(GameSprite);
}
public void Login_Click()
{
if (sfs == null || !sfs.IsConnected)
{
sfs = new SmartFox();
sfs.ThreadSafeMode = true;
sfs.AddEventListener(SFSEvent.CONNECTION, OnConnection);
sfs.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
sfs.AddEventListener(SFSEvent.LOGIN, OnLogin);
sfs.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
sfs.AddEventListener(SFSEvent.ROOM_JOIN, OnJoinRoom);
sfs.AddEventListener(SFSEvent.ROOM_JOIN_ERROR, OnJoinRoomError);
sfs.AddEventListener(SFSEvent.EXTENSION_RESPONSE, GetResult);
ConfigData cfg = new ConfigData();
cfg.Host = defaultHost;
cfg.Port = defaultTcpport;
cfg.Zone = "BasicExamples";
cfg.Debug = true;
Debug.LogError("defaultHost " + defaultHost);
Debug.LogError("defaultTcpport " + defaultTcpport);
sfs.Connect(cfg);
}
}
void OnLogin(BaseEvent evt)
{
Debug.Log("Login Success");
sfs.Send(new JoinRoomRequest("The Lobby"));
}
void OnJoinRoom(BaseEvent evt)
{
Debug.Log("Joined Room"+ evt.Params["room"]);
}
void OnJoinRoomError(BaseEvent evt)
{
Debug.Log("Join Room Error" + evt.Params["errorMessage"]);
}
void OnLoginError(BaseEvent evt)
{
Debug.Log("Login Error"+ evt.Params["errorMessage"]);
}
void OnConnection(BaseEvent evt)
{
if ((bool)evt.Params["success"])
{
Debug.Log("Connection Success");
sfs.Send(new LoginRequest(Username, "", Zonename));
}
else
{
Debug.Log("Connection Error");
}
}
void OnConnectionLost(BaseEvent evt)
{
}
Your problem is that all of your clients have the same username when you do LoginRequest.
SFS automatically disconnect other users with the same username.
You must create a unique username for all of your clients to they can connect together.
The simplest way to do this is to use the device id as a username.
sfs.Send(new LoginRequest(SystemInfo.deviceUniqueIdentifier, "", Zonename));
hope this helps.
I have an int variable called "Stash". When I beat the level, the game will save. When it saves, I want Stash goes up by however much my score was that level. Currently, my code isn't saving my stash value, and it just sets it to "0(stash default) + Score(whatever my score was that level)." How can I write within this code to make my goal happen?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[RequireComponent(typeof(GameData))]
public class SaveScript : MonoBehaviour
{
private GameData gameData;
private string savePath;
void Start()
{
gameData = GetComponent<GameData>();
savePath = Application.persistentDataPath + "/gamesave.save";
}
public void SaveData()
{
var save = new Save()
{
SavedStash = gameData.Stash + gameData.Score //<------ (This code)
};
var binaryFormatter = new BinaryFormatter();
using (var fileStream = File.Create(savePath))
{
binaryFormatter.Serialize(fileStream, save);
}
Debug.Log("Data Saved");
}
public void LoadData()
{
if (File.Exists(savePath))
{
Save save;
var binaryFormatter = new BinaryFormatter();
using (var fileStream = File.Open(savePath, FileMode.Open))
{
save = (Save)binaryFormatter.Deserialize(fileStream);
}
gameData.Stash = save.SavedStash; //<---------- (and this code)
gameData.ShowData();
Debug.Log("Data Loaded");
}
else
{
Debug.LogWarning("Save file doesn't exist");
}
}
}
Also relevant stuff in my GameData file:
public int Stash { get; set; }
public int StashNew { get; set; }
public int Score { get; set; }
The StashNew was just an idea I had to get the whole thing working.
First of all: Never simply use + "/" for system file paths!
Different target devices might have different file systems with different path separators.
Rather use Path.Combine which automatically inserts the correct path separators depending on the OS it is running on.
savePath = Path.combine(Application.persistentDataPath, "gamesave.save");
Then to the main issue:
Properties like
public int Stash { get; set; }
public int StashNew { get; set; }
public int Score { get; set; }
are not de-/serialized by the BinaryFormatter!
Therefore when you do
binaryFormatter.Serialize(fileStream, save);
the numbers are not even written to the output file!
Then when reading in the same thing doesn't work and your properties simply keep the int default value of 0.
Simply remove these { get; set; } in order to convert your properties into serializable Fields and you should be fine.
public int Stash;
public int StashNew;
public int Score;
Just for the demo I used this code
public class MonoBehaviourForTests : MonoBehaviour
{
[SerializeField] private GameData In;
[SerializeField] private GameData Out;
[ContextMenu("Test")]
private void Test()
{
var formatter = new BinaryFormatter();
using (var fs = File.Open(Path.Combine(Application.streamingAssetsPath, "Example.txt"), FileMode.OpenOrCreate, FileAccess.Write))
{
formatter.Serialize(fs, In);
}
using (var fs = File.Open(Path.Combine(Application.streamingAssetsPath, "Example.txt"), FileMode.Open, FileAccess.Read))
{
Out = (GameData)formatter.Deserialize(fs);
}
}
[Serializable]
private class GameData
{
public int Stash;
public int StashNew;
public int Score;
}
}
As you can see this has also the side-efect that now the fields actually appear in the Inspector and can be edited. I write values only to the In instance and after hitting Test you can see them loaded into the Out instance via the file.
However in general I wouldn't recommend to use the BinaryFormatter here at all ... it adds a lot of overhead garbage you don't need at all. Simply go for e.g. JSON or a comparable simple file format like XML or CSV which only store the relevant data.
The save state class :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public class SaveState
{
public float RotationX { set; get; }
public float RotationY { set; get; }
public float RotationZ { set; get; }
public DateTime LastSaveTime { set; get; }
}
The save manager :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System;
using System.IO;
public class SaveManager : MonoBehaviour
{
#region Instance
private static SaveManager instance;
public static SaveManager Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<SaveManager>();
if (instance == null)
{
instance = new GameObject("Spawned SaveManager", typeof(SaveManager)).GetComponent<SaveManager>();
}
}
return instance;
}
set
{
instance = value;
}
}
#endregion
[Header("Logic")]
[SerializeField] private string saveFileName = "data.dat";
[SerializeField] private bool loadOnStart = true;
private BinaryFormatter formatter;
private SaveState state;
public SaveState State { get => state; set => state = value; }
private void Start()
{
// Init the formatter.
formatter = new BinaryFormatter();
DontDestroyOnLoad(this.gameObject);
// If loadOnStart is true try to load the saved file.
if (loadOnStart == true)
{
Load();
}
}
public void Save()
{
// If no state
if (state == null)
{
state = new SaveState();
}
//Set the time when trying to save.
state.LastSaveTime = DateTime.Now;
//Open the file.
var file = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Write);
formatter.Serialize(file, state);
file.Close();
}
public void Load()
{
// Open file
try
{
var file = new FileStream(saveFileName, FileMode.Open, FileAccess.Read);
// Read file.
state = (SaveState)formatter.Deserialize(file);
file.Close();
}
catch
{
Debug.Log("No save file found, creating a new entry");
Save();
}
}
}
And a test script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SaveTest : MonoBehaviour
{
private void Update()
{
SaveManager saveManager = SaveManager.Instance;
if(Input.GetKeyDown(KeyCode.T))
{
var player = GameObject.Find("Player");
saveManager.State.RotationX = player.transform.position.x;
saveManager.State.RotationY = player.transform.position.y;
saveManager.State.RotationZ = player.transform.position.z;
saveManager.Save();
}
if(Input.GetKeyDown(KeyCode.L))
{
SaveManager.Instance.Load();
}
}
}
When I press the T key I used a break point it's getting there finding the player.
But when pressing the L key it does nothing it's not changing the player rotation to what have been saved.
I can see the file is created data.dat 256 bytes size.
You are only loading in the data from your save file, but not actually doing anything with the data. If you want to set the player's rotation based on whatever data is inside your downloaded state you need to make a call to that data.
public void Load()
{
// Open file
try
{
var file = new FileStream(saveFileName, FileMode.Open, FileAccess.Read);
// Read file.
state = (SaveState)formatter.Deserialize(file);
file.Close();
//After obtaining the SaveState we apply the values to our player's local rotation
player.transform.localRotation = Quaternion.Euler(state.RotationX, state.RotationY, state.RotationZ);
}
catch
{
Debug.Log("No save file found, creating a new entry");
Save();
}
}
(Note that although i'm assigning the values to the player inside your Load method here, I would recommend doing that in a separate method for cleaner code)
So I'm only a week into learning C# and I've been working on this one problem for 8 hours. I'll describe what I'm trying to do, and if there's a better way I would really appreciate any guidance.
In the code below, I'm storing info from a Firebase database called snapshot, and trying to save it as a text file data.json. Later in the code, it reads data.json and calls allRoundData from the file.
Right now in Unity the application doesn't compile and says "Object reference not set to an instance of an object" for allRoundData = loadedData.allRoundData;
I also checked and nothing is writing to the text file when I open it in explorer.
I'm pulling trivia questions and answers from a Firebase database, so however I do this it's important that the user can't access the answers. Would it be more secure to skip writing it to a text file and just declare allRoundData = snapshot.allRoundData? The problem is that allRoundData is a string array and the snapshot is an object. Is there a way to easily convert it or how can I solve this in the simplest way?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
using Firebase;
using Firebase.Unity.Editor;
using Firebase.Database;
public class DataController : MonoBehaviour
{
public class FirebaseStart: MonoBehaviour
{
void Start()
{
// Set up the Editor before calling into the realtime database.
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("https://FIREBASEDATABASE");
// Get the root reference location of the database.
DatabaseReference reference =
FirebaseDatabase.DefaultInstance.RootReference;
FirebaseDatabase.DefaultInstance
.GetReference("allRoundData")
.GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
string gameDataFileName = "data.json";
string filePath =
Path.Combine(Application.streamingAssetsPath, gameDataFileName);
DataSnapshot snapshot = task.Result;
// Do something with snapshot...
string rawData = snapshot.GetRawJsonValue();
StreamWriter sw = new StreamWriter(filePath);
sw.WriteLine(rawData);
sw.Close();
}
});
}
}
private RoundData[] allRoundData;
private PlayerProgress playerProgress;
// Use this for initialization
void Start ()
{
DontDestroyOnLoad(gameObject);
LoadGameData();
LoadPlayerProgress();
SceneManager.LoadScene("MenuScreen");
}
public RoundData GetCurrentRoundData()
{
return allRoundData [0];
}
public void SubmitNewPlayerScore(int newScore)
{
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
public int GetHighestPlayerScore()
{
return playerProgress.highestScore;
}
// Update is called once per frame
void Update () {
}
private void LoadPlayerProgress()
{
playerProgress = new PlayerProgress();
if (PlayerPrefs.HasKey("highestScore"))
{
playerProgress.highestScore = PlayerPrefs.GetInt("highestScore");
}
}
private void SavePlayerProgress()
{
PlayerPrefs.SetInt("highestScore", playerProgress.highestScore);
}
private void LoadGameData()
{
string gameDataFileName = "data.json";
string filePath = Path.Combine(Application.streamingAssetsPath, gameDataFileName);
if (File.Exists(filePath))
{
string dataAsJson = File.ReadAllText(filePath);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
}
else
{
Debug.LogError("Cannot load game data!");
}
}
}
I'm building my first prototype for multi-game sharing achievement system. It means there's two or more games using one shared achievement data. I'm storing the data in an .xml file, for now the xml only contains coins data.
So i want my program to load and modify the amount of coins from it.
This is My XML :
<Achievement><Coins>800</Coins></Achievement>
My Code :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
public class XMLTest : MonoBehaviour {
string Filelocation, FileName;
private Text CoinsText;
public int Coins;
public int Price;
void Start () {
CoinsText = GameObject.Find("CoinsText").GetComponent<Text>();
Filelocation = "E:/Projek/Projek Pak Suprapedi/XML Test/";
FileName = "Coins.xml";
}
public void WriteToXML()
{
XmlDocument XmlDoc = new XmlDocument();
if(File.Exists(Filelocation + FileName))
{
XmlDoc.Load(Path.Combine(Filelocation, FileName));
XmlNodeList AchievementList = XmlDoc.GetElementsByTagName("Achievement");
foreach(XmlNode node in AchievementList)
{
if(node.Name == "Coins")
{
node.InnerText = Coins.ToString();
}
}
XmlDoc.Save(Path.Combine(Filelocation, FileName));
}
}
public void LoadFromXML()
{
XmlDocument XmlDoc = new XmlDocument();
if(File.Exists(Filelocation + FileName))
{
XmlDoc.Load(Path.Combine(Filelocation,FileName));
XmlNodeList AchievementList = XmlDoc.GetElementsByTagName("Achievement");
foreach(XmlNode node in AchievementList)
{
if(node.Name == "Coins")
{
Coins = int.Parse(node.InnerText);
}
}
}
}
public void Buy()
{
Coins -= Price;
}
public void GetMoney()
{
Coins += Price;
}
void Update()
{
CoinsText.text = "Your Coins : " + Coins.ToString();
}
}
I'm assigning the public void to a button so i can modify the coins then save or load it. But it seems that load and save function won't do anything, nothing happen to xml file.
I will deploy my game to Android platform, but I don't know if this method would work, would it ?
I will answer my own question. I've found the problem.
so the problem is :
XmlNodeList AchievementList = XmlDoc.GetElementsByTagName("Achievement");
I'm replacing it with
XmlNodeList AchievementList = XmlDoc.GetElementsByTagName("Coins");
And it works !!
I think you can understand what is the problem now