Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 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 Eco-Editor · Jan 26, 2020 at 02:30 PM · loadsave datamultiple objectssystem.iobinary

How to load multiple entries from one Binary file?

Hello all,

In my game I save and load data using Binary file. The save and load of one item was successful for me, however my goal is to save each passed level and once the game is over, show the player their gaming summary of all passed levels and the score for each level. This is the code so far (based in this tutorial):

 using System;
 
 [Serializable]
 public class Player_Tutorial
 {
     private string _itemName;
     private int _itemNumber;
     private float _score;
 
     public Player_Tutorial() { }
 
     public Player_Tutorial(string name, int number, float score)
     {
         _itemName = name;
         _itemNumber = number;
         _score = score;
     }
 
     public string ItemName { get { return _itemName;} set {  _itemName = value;}} 
  //similar get/set

Data code:

 using System;
 using System.IO;
 using System.Runtime.Serialization.Formatters.Binary;
 using UnityEngine;
 
 public class SavingData_Tutorial : MonoBehaviour
 {
     private Player_Tutorial myPlayer;
     private string DATA_PATH = "/MyGame.dat";
     private string path; 
 
     private void Start()
     {
         path = Application.persistentDataPath + DATA_PATH;
         //SaveData();
         LoadData();
         if (myPlayer!=null)
         {
             print("Player name: " + myPlayer.ItemName + " Player itemNumber: " + myPlayer.ItemNumber + " Player nw: " + myPlayer.Score);
         }
     }



     //this will called once a level is completed
     void SaveData()
     {
         FileStream file = null;
         try
         {
             BinaryFormatter bf = new BinaryFormatter();
             file = new FileStream(path, FileMode.Append);
             Player_Tutorial p = new Player_Tutorial("The wood", 1, 30);//for testing
 
             bf.Serialize(file, p); //store this data at this file location
         }
 
         catch (Exception e)
         {
             if (e !=null)
             {
                 Debug.Log("File not found: " + e.Message);
             }
         }
         finally
         {
             if (file != null) file.Close();
         }
     }
 
     void LoadData()
     {
         FileStream file = null;
         try
         {
             BinaryFormatter bf = new BinaryFormatter();
             file = File.Open(path, FileMode.Open);
             myPlayer = bf.Deserialize(file) as Player_Tutorial;
             
         }
         catch(Exception e)
         {
             Debug.Log("Could not read from file: " + e.Message);
         }
 
         finally
         {
             if (file != null) file.Close();
         }
     }
 }

The goal is to show the summary of each entry in a UI canvas at the end of the game, for example:

"The wood", level 1 - score: 30

"The ocean", level 2 - score:10

"The moon", level 3 - score: 50

Is it a good idea to use binary files for that?

Thank you

Comment
Add comment · Show 5
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 sacredgeometry · Jan 26, 2020 at 04:57 PM 0
Share

There is no reason why you cant save what every you want as binary but...

Does it need to be binary data? I try to stay away from using binary file types for this sort of application. There are way easier ways to serialise and deserialise data in unity outside of the standard persistence methods which are less prohibitive. I.e. the amount of times having files in a plain text format has saved me by being able to debug and fix corrupted project files or save states.

avatar image sacredgeometry · Jan 28, 2020 at 01:43 AM 1
Share

I just made you a 17 $$anonymous$$ute video explaining it. Sorry if its a bit ranty and wombly its 1 am and I have work in the morning.

https://www.youtube.com/watch?v=_xLjhjmIGlo

avatar image Eco-Editor sacredgeometry · Feb 02, 2020 at 07:58 PM 0
Share

Thanks for the video. Please keep it on youtube for a while.

avatar image sacredgeometry Eco-Editor · Feb 02, 2020 at 08:58 PM 1
Share

It's unlisted I wont remove it so not to worry.

Show more comments

1 Reply

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

Answer by sacredgeometry · Jan 26, 2020 at 05:47 PM

So the premise is simple. Your problem is that you are trying to write multiple blobs of data into a binary file with their headers.


You dont need to do this: What you should do instead is write a collection/ array into the file instead (or use composition to create a SaveState object that contains all the data you want in its fields/ members. )


On save: You dont need to append to the file you just need to wipe it and save the whole blob of data in the file again.

On load: you just need to deserialise the file into the same collection/ array type and it will all work as demonstrated below.


 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 using System.Runtime.Serialization.Formatters.Binary;
 using UnityEngine;
 
 public class FileSaver<TSaveFile>
 {
     public List<TSaveFile> Data = new List<TSaveFile>();
     private string _path;
 
     public FileSaver(string path)
     {
         if(typeof(TSaveFile).GetCustomAttributes(typeof(SerializableAttribute), false).Length == 0)
             throw new NotSupportedException($"{nameof(TSaveFile)} must be be decorated with a SerializableAttribute");
             
         _path = path;
         LoadData();
     }
 
     public void SaveData(params TSaveFile[] data)
      {
          Data.AddRange(data);
 
          using(FileStream stream = File.Create(_path))
          {
             BinaryFormatter formatter = new BinaryFormatter();
             formatter.Serialize(stream, Data);
          }
      }
  
      public IEnumerable<TSaveFile> LoadData()
      {
          if(!File.Exists(_path)) SaveData();
 
          using(FileStream stream = File.OpenRead(_path))
          {
              if(stream.Length != 0)
              {
                 BinaryFormatter formatter = new BinaryFormatter();
                 Data = formatter.Deserialize(stream) as List<TSaveFile>;
              }
          }
 
          return Data;
      }
 }
 
 
 public class SaveManager : MonoBehaviour
 {
     FileSaver<PlayerTutorial> PlayerTutorialSaver = new FileSaver<PlayerTutorial>("playerTutorials.save");   
 
     void Update()
     {
         if(Input.GetKeyDown(KeyCode.Q))
         {
             PlayerTutorialSaver.SaveData(new PlayerTutorial{
                 SceneName = "Wooter"
             });
         }   
         else if(Input.GetKeyDown(KeyCode.W))
         {
             var data = PlayerTutorialSaver.LoadData();
             // NOTE: Use data here
             Debug.Log(string.Join(", ", data.Select(d => d.SceneName)));
         }
     }
 }
 



Comment
Add comment · Show 17 · 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 Eco-Editor · Jan 26, 2020 at 07:07 PM 0
Share

Hi @sacredgeometry

Thank you for your answer, it thought me a lot.

I've tried you method, and its difficult for me to show the output of the LoadData().

This is how my code looks now:

 public class SavingData_Tutorial : $$anonymous$$onoBehaviour
 {
     public List<Player_Tutorial> playerData;
     private string DATA_PATH = "/$$anonymous$$yGame.dat";
     private string path; 
 
 
     private void Start()
     {
         path = Application.persistentDataPath + DATA_PATH;
         playerData = new List<Player_Tutorial>();
     }
     public void SaveData()
     {
         FileStream stream;
         if (!File.Exists(path))
         {
             stream = new FileStream(path, File$$anonymous$$ode.Create);
         }
         else
         {
             stream = new FileStream(path, File$$anonymous$$ode.Append);
         }
 
         BinaryFormatter formatter = new BinaryFormatter();
         formatter.Serialize(stream, playerData);
         stream.Close();
     }
 
     public List<Player_Tutorial> LoadData()
     {
         List<Player_Tutorial> output;
         using(FileStream stream = File.OpenRead(path))
         {
             BinaryFormatter formatter = new BinaryFormatter();
             output = formatter.Deserialize(stream) as List<Player_Tutorial>;
         }
        
         return output;
     }
 
 }



and this is the manager code:

     void Update()
     {
         //Save data
         if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.Q))
         {
             Player_Tutorial pt = new Player_Tutorial();
             //pt.SceneName = "The_Woods";
             //pt.Number =1;
             //pt.Boolean = true;           
             pt.SceneName = "The_Sea";
             pt.Number = 2;
             pt.Boolean = false;
             data$$anonymous$$.playerData.Add(pt);
             print("saving data ");
             data$$anonymous$$.SaveData();
         }
 
         //Load data
         if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.W))
         {
             data$$anonymous$$.LoadData();
 
             //Need to see the loaded data but it's not working:
             Player_Tutorial [] names = data$$anonymous$$.LoadData().ToArray();
             for (int i = 0; i < names.Length; i++)
             {
                 print("items are:  " + names[i]);
             }
 
         }
     }
 

I don't now how to "reveal" or "unwrap" the variables in the class "Player_Tutorial" in the List

I need to be able to see: SceneName[0] = "The_woods", SceneName[1] = "The_Sea" etc...

How to get to the variables in the list? all it gives me: "items are: Player_Tutorial"

I can't get the nice look you get when assigning var loadedData = LoadData()

avatar image sacredgeometry Eco-Editor · Jan 26, 2020 at 07:27 PM 0
Share

The same way you would access anything in a array it has an indexer and is itterable.

To access the specific thing at a specific index you would use the [] syntax.

so for the first item it would be

loadedData[0]

Then you would address its properties like

loadedData[0].$$anonymous$$yProperty

That said you will get an out of range exception if you try to access an item at an index larger than the collection size so what you could do is iterate over the collection with a loop.

If your collection is always going to be the same size you could make them to another class that has a set amount of properties/ fields of the type in your collection and just set them like:

 $$anonymous$$yClass.$$anonymous$$yPropertyA = loadedData[0];
 $$anonymous$$yClass.$$anonymous$$yPropertyB = loadedData[1];

etc.

Is that what you were asking?


Alternatively you could use ElementAtOrDefault

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.elementatordefault?view=netframework-4.8


This would let you null coalesce or propagate like:

 var name = $$anonymous$$yArray?.ElementAtOrDefault(1)?.Name;

This way it wont throw an exception but ins$$anonymous$$d just set name to null if either the array is null or there is no element at index 1.

Similarly if you wanted to set a default value in that case you could do

     var name = $$anonymous$$yArray?.ElementAtOrDefault(1)?.Name ?? "Default Name";

avatar image sacredgeometry Eco-Editor · Jan 26, 2020 at 07:35 PM 0
Share

p.s you dont need the ToArray() to iterate over a List. Its an IEnumerable

The Length Property is just a $$anonymous$$ethod called Count().

avatar image Eco-Editor sacredgeometry · Jan 26, 2020 at 08:02 PM 0
Share

Something is not working for me.

         if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.W))
         {
             var data = data$$anonymous$$.LoadData();
 
             //Need to see the loaded data
             print(data.Count);
 
         }

Doing this, showed me the list has only one object.

Then I use the method as it is:

     public void SaveData()
     {
         FileStream stream;
         if (!File.Exists(path))
         {
             stream = new FileStream(path, File$$anonymous$$ode.Create);
         }
         else
         {
             stream = new FileStream(path, File$$anonymous$$ode.Append);
         }
 
         BinaryFormatter formatter = new BinaryFormatter();
         formatter.Serialize(stream, new List<Player_Tutorial>
         {
             new Player_Tutorial
             {
                 SceneName = "The_Woods",
                 Number = 1,
                 Boolean = true
             },
             new Player_Tutorial
             {
                 SceneName = "The_$$anonymous$$oon",
                 Number = 2,
                 Boolean = false
             }
         });
         stream.Close();
     }
 
     public List<Player_Tutorial> LoadData()
     {
         List<Player_Tutorial> output;
         using(FileStream stream = File.OpenRead(path))
         {
             BinaryFormatter formatter = new BinaryFormatter();
             output = formatter.Deserialize(stream) as List<Player_Tutorial>;
         }
        
         return output;
     }


No matter how many times I save, or even if the string SceneName is changed, the list count is now 2, when it of course has more than two entries.

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

120 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 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 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 avatar image avatar image avatar image

Related Questions

Save Load Data unity 0 Answers

im getting a serialization exception eror in my load method 0 Answers

I need a solution to SQL, saving and loading data and security/antipiracy 4 Answers

Save and Load System of multiple objects in scene? 2 Answers

How to serialize/deserialize a class without knowing its type 1 Answer


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