Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by BugFighter · Dec 21, 2015 at 07:19 PM · androidserializationpersistencebinary

Serialization is incorrect on Android

Hello there. I want to serialize game data for an Android game. I had to change some things about my first script that was working, but now I have problems with that script. I am sure there is a much smoother way of doing this:

  1. I want to store my data in a "DataHandleScript" at runtime.

  2. I want to store my data in serialized binary between game sessions.

I tried to strip this whole thing down into one script, but I had some problems. I wanted to be able to call all variables without reference (-> static variables would be easy), but they are supposed to be serializable (so static is not possible).

Example:

 SaveValues.highscore = x;  //or SaveValues.Instance.highscore
 SaveValues.Save();

I've tried following multiple tutorials, but they didn't guide me to the solution I wanted, therefore I tried changing their suggestions a little. Only problem is that if I load another scene or reopen the app, the values have randomly increased a lot (e.g. from 115 scored points to 22.465).

This is the main part of my Saving Script:

 [System.Serializable]
 public static class SavingValues
 {
         
 public static SaveValues Instance;
 
 void Awake(){
     Instance = this;
 }
 
 public void Load()
 {
     if (File.Exists(Application.streamingAssetsPath+"/SaveFile.bfp"))
     {
         SaveValues.Instance.data = SaveData.Load(Application.persistentDataPath+"/"+SaveValues.Instance.fileName+".bfp");
         
         SaveValues.Instance.highscore = SaveValues.Instance.data.GetValue<int>("Highscore");
         SaveValues.Instance.collectedPoints = SaveValues.Instance.data.GetValue<int>("CollectedPoints");
         SaveValues.Instance.shopUnlocked = SaveValues.Instance.data.GetValue<bool>("ShopUnlocked");
         SaveValues.Instance.lifetimeScore = SaveValues.Instance.data.GetValue<int>("LifetimeScore");
     }
 }
 
 public void Save()
 {
     SaveValues.Instance.data = new SaveData(SaveValues.Instance.fileName);
     
     SaveValues.Instance.data["Highscore"] = SaveValues.Instance.highscore;
     SaveValues.Instance.data["CollectedPoints"] = SaveValues.Instance.collectedPoints;
     SaveValues.Instance.data["ShopUnlocked"] = SaveValues.Instance.shopUnlocked;
     SaveValues.Instance.data["LifetimeScore"] = SaveValues.Instance.lifetimeScore;
     
     SaveValues.Instance.data.Save();
 }
 
 public void Reset(bool firstStartDone)
 {
     SaveValues.Instance.data = new SaveData(SaveValues.Instance.fileName);
     
     SaveValues.Instance.data["Highscore"] = 0;
     SaveValues.Instance.data["CollectedPoints"] = 0;
     SaveValues.Instance.data["ShopUnlocked"] = false;
     SaveValues.Instance.data["LifetimeScore"] = 0;
     SaveValues.Instance.data["FirstStartDone"] = firstStartDone;
     
     SaveValues.Instance.data.Save();
 }

And this is my DataHandleClass:

 [System.Serializable]
 public class SaveValues
 {
     public int highscore = 0;                //saved Highscore
     public int collectedPoints = 0;            //Points collected on this account
     public bool shopUnlocked = false;        //Is shop unlocked
     public int lifetimeScore = 0;            //count of all points ever collected
     
     public string fileName = "SaveFile";            //name of the save file
     private SaveData data;                //save data variable
 }

in the same script. I hope you can show me a better way of archieving my goal or find the reason for this script to strangely increasing my values. Please help me, I have made great progress in my game but I'm stuck with this problem for a few weeks now and I can't find a proper solution.

Thanks in advance.

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Dec 22, 2015 at 12:49 PM 0
Share

There are alot strange / odd things in your code:

  • You have a static class "SavingValues" and have the attribute Serializeble attached. That makes no sense since a static class can only contain static members. You can't have an instance of a static class and therefore it can't be serialized.

  • That static class seems to contain non static members like Awake, Load, Save, Reset. That wouldn't even compile. Given those names i guess that class should be a non static $$anonymous$$onoBehaviour derived class?

  • Your "SaveValues" class contains a field "fileName". Are you sure you want to store the filename inside the savegame? It seems a bit odd to serialize the filename inside the file itself.

  • You use SaveValues.Instance but the SaveValues class doesn't have an Instance. Your SavingValues class does.

  • This line Instance = this; in awake won't work since your would try to assign a SavingValues instance (given that you removed the static from the class declaration) to a variable of type SaveValues.

  • What is actually SaveData? It seems you have a mix of 3 classes (where SaveData is missing completely) which actually should represent 2.

avatar image BugFighter Bunny83 · Dec 22, 2015 at 03:48 PM 0
Share

You are right, there are a few mistakes in my question, I had to scratch a few parts of the script that were messy remains of a script that was altered completely nearly a hundret times :D Thanks for the heads up!

2 Replies

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by BugFighter · Mar 03, 2016 at 12:25 PM

I've figured it out. My problem wasn't this script (although It would have been a problem too ^^). In a FixedUpdate()-Function of mine a script has done this:

 void FixedUpdate ()
     {
         if (gameover == true)
         {
             scoreText.text = "Score: " + score;                                                             
             GameOverText.text = "GAME OVER";                                                                 
             FinalScoreText.text = "You've earned " + score + " points.";                                                    
         Instance.collectedPoints += score;
         SaveValues.Save();
         }
     }

Which ultimately leads to a repeated addition of the score to the collectedPoints variable until I load another scene. I've fixed that and now it works!

Thanks though, @Bunny83, your answers were extremely helpful too.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by Bunny83 · Dec 22, 2015 at 01:18 PM

A common implementation is to use a simple singleton class for both, storing the values with a BinaryFormatter and provide global access to those values.

 //SaveLoadManager.cs
 using UnityEngine;
 using System.Runtime.Serialization.Formatters.Binary;
 using System.IO;
 
 [System.Serializable]
 public class SaveValues
 {
     public int highscore = 0;
     public int collectedPoints = 0;
     public bool shopUnlocked = false;
     public int lifetimeScore = 0;
     public bool FirstStartDone = false;
     private static string m_DefaultFileName = "SaveFile.bfp";
     private static SaveValues m_Instance = null;
     public static SaveValues Instance
     {
         get
         {
             if (m_Instance == null)
             {
                 if (!Load())
                 {
                     m_Instance = new SaveValues();
                     Reset();
                 }
             }
             return m_Instance;
         }
     }
     public static void Save()
     {
         Save(Application.streamingAssetsPath + "/" + m_DefaultFileName);
     }
     public static void Save(string aFileName)
     {
         using (var stream = File.OpenWrite(aFileName))
         {
             var formatter = new BinaryFormatter();
             formatter.Serialize(stream, Instance);
         }
     }
     public static bool Load()
     {
         return Load(Application.streamingAssetsPath + "/" + m_DefaultFileName);
     }
 
     public static bool Load(string aFileName)
     {
         if (!File.Exists(aFileName))
             return false;
         using (var stream = File.OpenRead(aFileName))
         {
             var formatter = new BinaryFormatter();
             m_Instance = (SaveValues)formatter.Deserialize(stream);
         }
         return true;
     }
     public static void Reset()
     {
         Instance.highscore = 0;
         Instance.collectedPoints = 0;
         Instance.shopUnlocked = false;
         Instance.lifetimeScore = 0;
     }
 }
 
 public class SaveLoadManager : MonoBehaviour
 {
     void Awake()
     {
         SaveValues.Load();
     }
 }

Here you can access your values from everywhere using:

 SaveValues.Instance.highscore = 6;

If you don't like the "Instance" in between, you can add static properties for all your values like that:

 public static int highscore
 {
     get { return Instance.highscore; }
     set {Instance.highscore = value; }
 }
 //...



edit
Fixed an issue that would result in a stack overflow when the save files doesn't exist yet ^^.

Comment
Add comment · Show 10 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image BugFighter · Dec 22, 2015 at 03:46 PM 0
Share

That looks very promising! I'll give it a try and see if it fits my needs, thanks.

avatar image BugFighter · Dec 22, 2015 at 04:56 PM 0
Share

@Bunny83 I still have a few questions about exceptions I get.

  1. If I use Application.strea$$anonymous$$gAssetsPath, the error message reads something like "path .../Assets/StreamedAssets/SaveFile.bfp could not be found", but since I don't call SaveValues.Load() on Awake(), but later on – only as I checked if the file exists (which gives me an exception too; weird?). – shouldn't this error not occur then? Also why do I get an error if I only ask IF the file was existing? Shouldn't it just return false? Also, I thought that Application.strea$$anonymous$$gAssetsPath is compiled and therefore not editable for a mobile device or is that just an urban legend :D?

  2. The console gives me an error that says that the "field data is not found in the class SaveValues". I thought you just need to input data to be serialized, which in my case would be Instance.

Thanks again for your patience ^^

avatar image BugFighter BugFighter · Dec 23, 2015 at 07:13 PM 0
Share

@Bunny83 Actually, I've checked the error again. It says: DirectoryNotFoundException: Could not find a part of the path "/Users/username/Unity Project/Assets/Strea$$anonymous$$gAssets/SaveFile.bfp" and origins from the line

 if(File.Exists(Application.strea$$anonymous$$gAssetsPath + "/" + m_defaultFileName)
avatar image Bunny83 BugFighter · Dec 23, 2015 at 10:58 PM 0
Share

I just realised that you used strea$$anonymous$$gAssets. That path is used for assets that you provide inside the special folder strea$$anonymous$$gassets in your project. Those assets won't be packed into Unity's internal assetformat but are simply copied into the build folder.

If you don't have any strea$$anonymous$$g assets in your game, that folder most likely isn't created at all. Of course you could create that folder yourself, but it's actually not ment to be used for storing savegames. You should use Application.persistentDataPath which is ment as permanent storage location for your game.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

39 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

SerializationException: Type UnityEngine.UI.Button is not marked as Serializable. Solution 1 Answer

Serialized Fields go bye bye on Android 0 Answers

Serializer deletes saved info on game restart 1 Answer

Save multiple scriptableObjects in just one binary file 2 Answers

Keeping native data around across recompile 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges