Hello I want migration unity project version from 5.0.2 to 2020.x.x
when did directly migration to 2020.x.x I did run into a problem with expection
"InvalidCastException: Object must implement IConvertible."
So I did try migration Sequentially unity5 -> V2017 -> V2018 was working well
But when I migration v2019 I did run into a problem with same expection
this code is causing problems area
[System.Serializable]
public class BookStruct
{
public int ID;
public int[] PageComposition;
public int PageCount;
public string Name;
public int CoverPageID;
public int CategoryID;
public Dictionary<SystemLanguage, TextDataTemplate> dicTextData;
public int[] RecommendBookList;
public int MarketingMark;
public string RecommendAge;
public int Priority;
public bool FreeAtNow;
}
/* public static string BinaryStringFormatter(object target)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, target);
return BitConverter.ToString(ms.ToArray()); // not here!
} */
public static object BinaryDeserialize(byte[] data)
{
MemoryStream ms = new MemoryStream(data);
BinaryFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(ms); //here!
}
bottom code is before code
switch (Application.platform)
{
case RuntimePlatform.Android:
filepath = Application.streamingAssetsPath + "/"+bookname;
break;
default:
filepath = "file://" + Application.streamingAssetsPath + "/" + bookname;
break;
}
Debug.Log("파일 경로 확인 => " + filepath);
using (WWW www = new WWW(filepath))
{
while (!www.isDone)
{
yield return true;
}
TextAsset t = www.assetBundle.mainAsset as TextAsset;
book = (BookStruct)BK_Function.BinaryDeserialize(t.bytes);
www.assetBundle.Unload(true);
iBook.iPageCount = book.PageComposition.Length;
Debug.Log(book.dicTextData[SystemLanguage.Korean].Description);
Debug.Log("pagecount : " + book.PageCount + " // pageComposition.Length : " + book.PageComposition.Length);
}
Also,I did Debug.log file path and result is
" file://C:/unitySource/singlebook/Assets/StreamingAssets/B12_2_Cat.unity3d"
and Bookstruct is
""
Please help me (:
Related
I am new to stackoverflow and to Unity3D, so I am sorry if I am doing things wrong.
So currently, I am making a puzzle game. It has 50 different levels.
I need for each of them, to save 3 or 4 variables.
For example, when level 1 is cleared, I want it to store (int)hitCounts, (bool)cleared, (int)bestHitCounts.
I don't wanna use playerPrefs, as I don't want it to be readable from outside the box. I want it be converted to a binary file.
here is what I have :
#1 : made a static class TGameDat
[System.Serializable]
public class TGameDat
{
public int tGameDatInt;
public bool tGameDatBool;
public int tSceneIndex;
public TGameDat (TPlayer player)
{
tGameDatInt = player.tInt;
tGameDatBool = player.tBool;
tSceneIndex = player.tScene;
}
}
#2 : then made Tplayer(monobehaviour)
public class TPlayer : MonoBehaviour
{
public int tInt = 0;
public bool tBool = false;
public int tScene;
public List<TPlayer> TestGameDatList = new List<TPlayer>();
private void Start()
{
TSceneMaker();
}
public void TSceneMaker()
{
tScene = SceneManager.GetActiveScene().buildIndex;
}
public void TNextScene()
{
SceneManager.LoadScene(tScene + 1);
}
public void TPreviousScene()
{
SceneManager.LoadScene(tScene - 1);
}
public void TSaveVariables()
{
TSave.TSavePlayer(this);
TestGameDatList.Add(this);
Debug.Log("saved");
Debug.Log(tInt + " " + tBool + " " + tScene);
}
public void TLoadVariables()
{
List<TGameDat> data = TSave.TLoadPlayer(this);
Debug.Log("loaded. data count = " + data.Count + " tSceneIndex " + tScene);
tInt = data[0].tGameDatInt;
tBool = data[0].tGameDatBool;
tScene = data[0].tSceneIndex;
}
}
#3 : finally I created a save and load system :
public static class TSave
{
public static void TSavePlayer (TPlayer player)
{
BinaryFormatter formatter = new BinaryFormatter();
List<TGameDat> data = new List<TGameDat>();
string path = Application.persistentDataPath + "/Tsave_" + player.tScene + ".fun";
if(File.Exists(path))
{
FileStream stream = File.Open(path, FileMode.Open);
data.Add(new TGameDat(player));
formatter.Serialize(stream, data);
stream.Close();
}
else
{
FileStream stream = File.Create(path);
data.Add(new TGameDat(player));
formatter.Serialize(stream, data);
stream.Close();
}
}
public static List<TGameDat> TLoadPlayer(TPlayer player)
{
string path = Application.persistentDataPath + "/Tsave_" + player.tScene + ".fun";
if(File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = File.Open(path, FileMode.Open);
List<TGameDat> data = new List<TGameDat>();
data = formatter.Deserialize(stream) as List<TGameDat>;
stream.Close();
return data;
}
else
{
Debug.LogError("Save file not found in " + path);
return null;
}
}
}
So, here are my problems :
1 : in the current situation, each scene compiles a binary file. So at the end, it will have a bunch of binary files piled up... Like 50, as I have 50 scenes... isn't it too many?
2 : of course I tried the make a single save file using List, and each level would come to add its own variable data in it.
But instead of adding the data, it would simply replace the previous data. Then there is always only 1 index in the List.
Therefore, when I load, the variables are from the last played level! And when I try to play another level after playing the first level, because there is only 1 index in the list, I get out of range error.
How shall I approach this?
sorry for the long long text!
thank you for your inputs!
first thing first, do you really need every level to have its own save data? Because if you only need to store the data between one level and another I would suggest you use some kind of PlayerState class that stores the data of the previous level.
But if you really need to store the data of every level then I'll recommend you using a dictionary rather than a simple list.
Here is an example of how I would do it
Note: I haven't tested this code yet!
SaveGameManager class
public string SavePath => Application.persistentDataPath + "/save.dat";
public static SaveGameManager Instance; // Singleton pattern
private Dictionary<string, TGameDat> gameData;
private void Awake()
{
if (Instance != null)
{
Destroy(this.gameObject);
return;
}
// Singleton initialization
Instance = this;
// Keep the object when changing scenes
DontDestroyOnLoad(this.gameObject);
LoadGameData();
}
public TGameDat GetGameData(string key)
{
if (gameData.TryGetValue(key, out TGameDat data))
{
return data;
}
Debug.Log($"Unable to find saved data with key {key}");
return null;
}
public void SetGameData(string key, TGameDat data)
{
// Sets a value with given key and save it to file
gameData[key] = data;
SaveGameData();
}
public void SaveGameData()
{
Serializer.SaveBinaryFile(gameData, SavePath);
}
public void LoadGameData()
{
var savedData = Serializer.LoadBinaryFile(SavePath);
if (savedData != null)
{
gameData = (Dictionary<string, TGameDat>)savedData;
}
else
{
// Creating and saving new data because we can't found
// any that already stored in path
gameData = new Dictionary<string, TGameDat>();
SaveGameData();
}
}
And then, the Serializer class
public static void SaveBinaryFile(object data, string path)
{
using (var stream = new FileStream(path, FileMode.Create))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, data);
}
}
public static object LoadBinaryFile(string path)
{
if (!File.Exists(path))
{
// Trying to load a file that does not exist
return null;
}
using (var stream = new FileStream(path, FileMode.Open))
{
var formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
}
And then you can use it like this
public TGameDat data;
public void TSaveVariables()
{
SaveGameManager.Instance.SetGameData(level.id, this.data);
}
public void TLoadVariables()
{
var savedData = SaveGameManager.Instance.GetGameData(level.id);
if (savedData != null)
{
this.data = savedData;
}
else
{
// We don't have any save file for this level yet
savedData = new TGameDat();
}
}
You can change level.id to whatever identifier you wanted to use.
I want to save and load data from unity, so i follow how to save object attribute from https://www.youtube.com/watch?v=XOjd_qU2Ido but when i want to return the data to load the data that have saved, the data can't get implicitly.
Here the Coordinate_data Class
using UnityEngine;
[System.Serializable]
public class Coordinate_data
{
public float[] position;
public Coordinate_data (RandomNumber coordinate)
{
position = new float[3];
position[0] = coordinate.transform.position.x;
position[1] = coordinate.transform.position.y;
position[2] = coordinate.transform.position.z;
}
}
And here the randomNumber function is to random and show the data that already saved or random
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RandomNumber : MonoBehaviour
{
public int MinNum = 0;
public int MaxNum = 100;
public Text NilaiRandomX = null;
public Text NilaiRandomY = null;
public Text NilaiRandomZ = null;
public Vector3 position;
public int StepTime = 2;
void Start()
{
StartCoroutine(RandomNumGenerator());
}
IEnumerator RandomNumGenerator()
{
while (1 != 0)
{
yield return new WaitForSeconds(StepTime);
int X = UnityEngine.Random.Range(MinNum, MaxNum);
int Y = UnityEngine.Random.Range(MinNum, MaxNum);
int Z = UnityEngine.Random.Range(MinNum, MaxNum);
float nilaix = X;
float nilaiy = Y;
float nilaiz = Z;
position = new Vector3(nilaix, nilaiy, nilaiz);
transform.position = position;
//NilaiRandomX.text = + X;
NilaiRandomX.GetComponent<Text>().text = "" + X;
//NilaiRandomY.text = Y;
NilaiRandomY.GetComponent<Text>().text = "" + Y;
//NilaiRandomZ.text = Z;
NilaiRandomZ.GetComponent<Text>().text = "" + Z;
}
}
}
Here the code that to save and load data
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class SaveSystem
{
public static void saveCoordinate (RandomNumber coordinate)
{
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "coordinate.bin";
FileStream stream = new FileStream(path, FileMode.Create);
Coordinate_data data = new Coordinate_data(coordinate);
formatter.Serialize(stream, data);
stream.Close();
}
public static RandomNumber LoadPlayer()
{
string path = Application.persistentDataPath + "coordinate.bin";
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
Coordinate_data data = formatter.Deserialize(stream) as Coordinate_data;
stream.Close();
return data; // fail to return data in here
}
else
{
Debug.LogError("Save File Not Found in " + path);
return null;
}
}
}
Everything works fine except that I now get this error message on the "return person" in the if statement.
Cannot implicitly convert type 'Coordinate_data' to 'RandomNumber'
Can anyone please help? Thanks in advance.
Because you have declared function to return RandomNumber!
In this line:
public static RandomNumber LoadPlayer() // This function is supposed to return RandomNumber
{
...
Coordinate_data data = formatter.Deserialize(stream) as Coordinate_data;
return data; // And returning Coordinate_data.
}
It seems like you are new to C# or Unity, so here is pro tip: Believe what compiler is saying. When the compiler says Cannot implicitly convert type 'Coordinate_data' to 'RandomNumber', then there must be an error that you are using Coordinate_data type where supposed to be using RandomNumber.
I'm not sure that what's the desired action, but if you want to return Coordinate_data, then just change it's return type, like:
public static Coordinate_data LoadPlayer()
BTW, Implicit type conversion means, when something's type mismatches, compiler tries to interpret it with given type, like:
private void DoSomthingWithFloat(float x)
{
...
}
DoSomethingWithFloat(1);
In above example, parameter x is supposed to be float, but it's called with int value. so compiler implicitly casts it to float.
But in your case, there is no way to cast Coordinate_data to RandomNumber, so compiler has thrown error.
EDIT: sorry the code is working as intended. I just failed to test properly. Sorry for your inconvenience
I found some code here on SO to store and load objects (Code in the end). Storing the file is correctly working, but getting the file into object back again is not working when you have a list of objects:
Executing
Block b = loadFile<Block>("file");
Console.WriteLine(b.allCoins.Count); //is 0
results in an empty List. Checking the xml file they are all correctly stored, which means that the loading is somehow not working. How can you correctly load the object?
Here is the block class:
[Serializable]
public class Block {
public struct Coin {
public string owner;
public string name;
public Coin(string n, string o) {
owner = o;
name = n;
}
};
public int name;
public List<string> hashOfParticles;
public int numberOfTransactions;
public List<Coin> allCoins;
}
}
Here is how i load the file into objects:
public static T loadFile<T>(string fileName) {
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try {
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString)) {
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read)) {
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
} catch (Exception ex) {
//Log exception here
}
return objectOut;
}
Here is the code which stores the file:
public static void storeFile<T>(T serializableObject, string fileName) {
if (serializableObject == null) { return; }
try {
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream()) {
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
Form1.instance.addToLog("Storing \"" + fileName + "\" succesful");
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
Form1.instance.addToLog("Storing \"" + fileName + "\" NOT succesful");
}
}
I'd sincerely appreciate if you looked through some of my code as I can't seem to be able to fix it myself, and this will also hopefully help others understand serialization better.
I have four pieces of related code here, with anything seemingly irrelevant cut out. I'm trying to create a user profile in a set up where the user then chooses from a range of games to play. Only options/settings for the chosen games, in this case something to do with a fish, are saved.
The error in the title occurs in the 4th section [feel free to skip to that] which is the OptionsController.LoadOptions() function at
FishData.Store data = (FishData.Store)bf.Deserialize(fileOpt);
I have this at the top of all following sections:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
1) Profile data structure:
public class Profile { //Profile variables
public static string id;
public static string name;
public static string dob;
public static bool fishEnabled;
public static bool birdEnabled;
public static string stringTest;
[System.Serializable]
public class Store { //Profile variables
public string id;
public string name;
public string dob;
public bool fishEnabled;
public bool birdEnabled;
public string stringTest;
}
}
2) Fish data structure:
public class FishData { //static Fish variables to be used in game
public static bool sizeInc;
public static bool speedInc;
public static int level;
public static bool toxic;
public static bool handLeft;
public static bool timer;
public static int timerVal;
public static int percentReq;
public static int successReq;
public static string testStringTwo;
public static List<StatController.Session> sessions = new List<StatController.Session>();
[System.Serializable]
public class Store { //non static variable set for serialization
public bool sizeInc;
public bool speedInc;
public int level;
public bool toxic;
public bool handLeft;
public bool timer;
public int timerVal;
public int percentReq;
public int successReq;
public string testStringTwo;
public List<StatController.Session> sessions = new List<StatController.Session>();
}
}
3) The script where the data file was initially created and where there didn't seem to be problems:
public class ProfileController : MonoBehaviour {
public static string currentID;
//create ----
public InputField idField;
public InputField nameField;
public InputField dobField;
public Toggle fishToggle;
public Toggle birdToggle;
//open ------
public Button idTitleButton;
public Text nameText;
public Text dobText;
public Text testText;
public InputField numField;
void Save() { //saves new ID and declares required variables based on game choice
Debug.Log("id =" + idField.text + ", name = " + nameField.text + ", save");
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/" + idField.text + ".dat");
Profile.Store data = new Profile.Store(); //sets Profile variables
currentID = idField.text;
data.id = idField.text;
data.name = nameField.text;
data.dob = dobField.text;
data.fishEnabled = fishToggle.isOn;
data.birdEnabled = birdToggle.isOn;
Profile.id = idField.text;
Profile.name = nameField.text;
Profile.dob = dobField.text;
Profile.fishEnabled = fishToggle.isOn;
Profile.birdEnabled = birdToggle.isOn;
bf.Serialize(file, data); //saves Profile variables
if (fishToggle.isOn) {
FishData.Store dataFish = new FishData.Store(); //sets default Fish variables
dataFish.sizeInc = false;
dataFish.speedInc = false;
dataFish.level = 5;
dataFish.toxic = true;
dataFish.handLeft = false;
dataFish.timer = false;
dataFish.timerVal = 240;
dataFish.percentReq = 0;
dataFish.successReq = 0;
bf.Serialize(file, dataFish); //saves default Fish variables
Debug.Log("level = " + dataFish.level);
}
file.Close(); //closes save file
idSelectField.text = idField.text; //ensures current ID is used as selected ID where needed
EnableOpenProfile(); //loads new profile
}
void Load() {
if (File.Exists(Application.persistentDataPath + "/" + idField.text + ".dat")) { //loads save file
currentID = idField.text;
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/" + idField.text + ".dat", FileMode.Open);
Profile.Store data = (Profile.Store)bf.Deserialize(file);
nameField.text = data.name; //loads saved Profile details and settings
fishToggle.isOn = data.fishEnabled;
birdToggle.isOn = data.birdEnabled;
fishPlayButton.enabled = data.fishEnabled;
birdPlayButton.enabled = data.birdEnabled;
FishData.Store dataFish = (FishData.Store)bf.Deserialize(file); //loads saved Fish settings
FishData.sizeInc = dataFish.sizeInc;
FishData.speedInc = dataFish.speedInc;
FishData.toxic = dataFish.toxic;
FishData.timer = dataFish.timer;
FishData.level = dataFish.level;
FishData.timerVal = dataFish.timerVal;
FishData.percentReq = dataFish.percentReq;
FishData.successReq = dataFish.successReq;
FishData.handLeft = dataFish.handLeft;
file.Close(); //closes save file
nameText.text = "Name : " + data.name + " ID : " + data.id; //displays profile details
dobText.text = "DOB : " + data.dob;
}
else return;
}
}
4) The main script that produces the error when trying to load what was saved in the previous script:
public class OptionsController : MonoBehaviour {
public static bool handLeft = false;
public Toggle sizeIncToggle;
public Toggle speedIncToggle;
public Toggle toxicToggle;
public Toggle timerToggle;
public Toggle leftToggle;
public Toggle rightToggle;
public InputField levelField;
public InputField timerValField;
public InputField percentReqField;
public InputField successReqField;
void LoadOptions() {
if (File.Exists(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat")) {
BinaryFormatter bf = new BinaryFormatter();
FileStream fileOpt = File.Open(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat", FileMode.Open);
FishData.Store data = (FishData.Store)bf.Deserialize(fileOpt); //[[ERROR HERE]]
sizeIncToggle.isOn = data.sizeInc;
speedIncToggle.isOn = data.speedInc;
toxicToggle.isOn = data.toxic;
timerToggle.isOn = data.timer;
levelField.text = data.level.ToString();
timerValField.text = data.timerVal.ToString();
percentReqField.text = data.percentReq.ToString();
successReqField.text = data.successReq.ToString();
fileOpt.Close();
}
}
public void SaveOptions() {
Debug.Log("name = " + ProfileController.currentID + ", save");
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat");
FishData.Store data = new FishData.Store();
data.sizeInc = FishData.sizeInc;
data.speedInc = FishData.speedInc;
data.toxic = FishData.toxic;
data.timer = FishData.timer;
data.level = FishData.level;
data.timerVal = FishData.timerVal;
data.percentReq = FishData.percentReq;
data.successReq = FishData.successReq;
data.handLeft = FishData.handLeft;
bf.Serialize(file, data);
Debug.Log("level = " + FishData.level);
file.Close();
}
}
Thank you.
To ensure you are serializing/deserializing in the correct order it is best that you create a "holding" class which contains both a Fish.Store variable and Profile.Store variable. If you serialize and deserilize in different orders you will run into issues like trying to deserialize a Fish.Store object to Profile.Store.
Try creating another class with public variables of Fish.Store and Profile.Store, like:
public class ExampleClass
{
public Fish.Store FishStore;
public Profile.Store ProfileStore;
public ExampleClass()
{
}
}
Then do:
ExampleClass example = new ExampleClass();
example.ProfileStore = data;
example.FishStore = dataFish;
//File Create stuff, etc
bf.Serialize(file, example);
//File close stuff, etc
And to deserialize:
ExampleClass e = (ExampleClass)bf.Deserialize(fileOpt);
data = e.ProfileStore;
dataFish = e.FishStore;
If you only wish to serialize/deserialize one class at a time, it is best practice to read/write to/from one file per serialized class.
Correct me if I'm wrong but it seems you first save Profile.Store
and then you save FishData.Store.
Then on the load you try to retreive the FishData.Store when the Profile.Store is first in the file.
(I'm assuming you use the load from script 4 and the save in script 3.)
First time playing with serialization in C#... any help would be greatly appreciated!
The following are my generic serializer and deserializer:
public static string SerializeObject<T>(T objectToSerialize)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream memStr = new MemoryStream();
try
{
bf.Serialize(memStr, objectToSerialize);
memStr.Position = 0;
return Convert.ToBase64String(memStr.ToArray());
}
finally
{
memStr.Close();
}
}
public static T DeserializeObject<T>(string str)
{
BinaryFormatter bf = new BinaryFormatter();
byte[] b = System.Text.Encoding.UTF8.GetBytes(str);
MemoryStream ms = new MemoryStream(b);
try
{
return (T)bf.Deserialize(ms);
}
finally
{
ms.Close();
}
}
This is the object I am trying to serialize:
[Serializable()]
class MatrixSerializable : ISerializable
{
private bool markerFound;
private Matrix matrix;
public MatrixSerializable( Matrix m, bool b)
{
matrix = m;
markerFound = b;
}
public MatrixSerializable(SerializationInfo info, StreamingContext ctxt)
{
markerFound = (bool)info.GetValue("markerFound", typeof(bool));
matrix = Matrix.Identity;
if (markerFound)
{
//deserialization code
}
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
// serialization code
}
public Matrix Matrix
{
get { return matrix; }
set { matrix = value; }
}
public bool MarkerFound
{
get { return markerFound; }
set { markerFound = value; }
}
}
And an example of how am running it:
MatrixSerializable ms = new MatrixSerializable(Matrix.Identity * 5, true);
string s = Serializer.SerializeObject<MatrixSerializable>(ms);
Console.WriteLine("serialized: " + s);
ms = Serializer.DeserializeObject<MatrixSerializable>(s);
Console.WriteLine("deserialized: " + ms.Matrix + " " + ms.MarkerFound);
When I try to run this I get an error "SerializationException was unhandled: The input stream is not a valid binary format. The starting contents (in bytes) are: 41-41-45-41-41-41-44-2F-2F-2F-2F-2F-41-51-41-41-41 ..."
Any advice on what I am doing wrong or how to fix this would be greatly appreciated!
You are using Base64 to convert byte array to string and GetUtf8 bytes to convert from string back to byte array.
Replace System.Text.Encoding.UTF8.GetBytes(str); with Convert.FromBase64String(str);