Search Unity

Saving High Score Data to a file?

Discussion in 'Editor & General Support' started by MosquitoByte, Mar 15, 2020.

  1. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Ive been working on a high score list for the past few days and i'm almost done, i just need to save the data. I've been following Brackeys' tutorial found here:
    but my situation is a little different from his. I have a custom Class named ScoreClass which stores a string for the name of the user who got the high score and an int for their actual score. for the sake of organization, my script which actually displays the high scores is a different one from the Save and Load script. In the high score display script, there is a list:
    Code (CSharp):
    1. [SerializeField] public List<ScoreClass> HighScoreData = new List<ScoreClass>();
    which is then used to display the scores as such:
    Code (CSharp):
    1.  //set score names
    2.         scorenames[0].text = HighScoreData[4].name;
    3.         scorenames[1].text = HighScoreData[3].name;
    4.         scorenames[2].text = HighScoreData[2].name;
    5.         scorenames[3].text = HighScoreData[1].name;
    6.         scorenames[4].text = HighScoreData[0].name;
    7.         //set score numbers
    8.         scorenums[0].text = HighScoreData[4].number.ToString();
    9.         scorenums[1].text = HighScoreData[3].number.ToString();
    10.         scorenums[2].text = HighScoreData[2].number.ToString();
    11.         scorenums[3].text = HighScoreData[1].number.ToString();
    12.         scorenums[4].text = HighScoreData[0].number.ToString();
    now my problem is this: how to i convert from the data in a save file into a list? Currently it looks like this:
    Code (CSharp):
    1. public List<ScoreClass> LoadedScores = formatter.Deserialize(stream) as ScoreClass[];
    but this brings up errors, and also causes visual studio to tear apart all the code brackets, seen here: https://i.gyazo.com/025d9069c59fe12ca0111ed62e84018b.png
     
  2. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  3. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    So, this is what i ended up coming up with:
    Code (CSharp):
    1.   public List<ScoreClass> LoadData()
    2.     {
    3.         if (File.Exists(path))
    4.         {
    5.             FileStream stream = new FileStream(path, FileMode.Open);
    6.  
    7.             string[] loadednames = new string[5];
    8.             int[] loadedscores = new int[5];
    9.             loadedscores = formatter.Deserialize(stream) as int[];
    10.             loadednames = formatter.Deserialize(stream) as string[];
    11.             List<ScoreClass> loadedList = new List<ScoreClass>();
    12.             for(int i = 0; i <= 4; i++)
    13.             {
    14.                 loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    15.                
    16.             }
    17.             return loadedList;
    18.  
    19.         }
    20.         else
    21.         {
    22.             Debug.LogError("save file not found in: " + path);
    23.             return null;
    24.         }
    25.     }
    26.    
    27. }
    it shows no errors in the compiler, however when calling the function to load, the console shows a NullReferenceException on the line:
    Code (CSharp):
    1.  loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    Why would this be?
     
  4. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  5. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    making the variables public to view them in the editor, it seems the "loaded" arrays are coming out empty, despite me having done what i assume is the correct way to load them.
    Code (CSharp):
    1.     loadednames = new string[5];
    2.             loadedscores = new int[5];
    3.  
    4.             loadedscores = formatter.Deserialize(stream) as int[];
    5.             loadednames = formatter.Deserialize(stream) as string[];
    6.             List<ScoreClass> loadedList = new List<ScoreClass>();
    7.  
    8.          
    9.             for(int i = 0; i <= 4; i++)
    10.             {
    11.              
    12.                 loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    13.              
    14.             }
    https://gyazo.com/ac21b6473c0973894c5bff99b4815f0f


    edit: putting them in the for loop:
    Code (CSharp):
    1.   for(int i = 0; i <= 4; i++)
    2.             {
    3.                 loadedscores[i] = (int)formatter.Deserialize(stream);
    4.                 loadednames[i] = formatter.Deserialize(stream) as string;
    5.                 loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    6.                
    7.             }
    seems to have made them appear in the inspector, however, the variables in these are coming up empty: https://i.gyazo.com/2b2ae3ead7573266c3de4dc265e4fe2f.png
    and causes an error in the console: https://i.gyazo.com/82db9b49e976b9b069d54c6fc5098341.png

    what gives?
     
  6. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    bump, the only reason i can think this would be happening is because maybe Formatter.deserialize doesnt know what to do? is that it?
     
  7. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Noticably, when attempting to deserialize the integers in the save, like so:
    Code (CSharp):
    1.  loadedscores[i] = formatter.Deserialize(stream) as int;
    brings up the error: "The as operator must be used with a reference type or nullable type(int is a non-nullable value type" but trying it the other way:
    Code (CSharp):
    1. loadedscores[i] = (int)formatter.Deserialize(stream);
    shows no errors in visual studio, but when attempting to run in the editor, it beings up a console error: "SerializationException: Attempting to deserialize an empty stream". How is it possible that my stream is empty? Didnt i do it right?
     
  8. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  9. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    here is the entire script in full so far, so that everyone knows whats going on:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.IO;
    5. using System.Runtime.Serialization.Formatters.Binary;
    6. public class SaveScript : MonoBehaviour
    7. {
    8.     public GameObject scoreDB; //reference to the in-scene score database
    9.     Scoreeditor scoreDBscript;
    10.     BinaryFormatter formatter = new BinaryFormatter();
    11.  
    12.     public string[] SaveNames;
    13.     public int[] SaveNums;
    14.  
    15.     public string[] loadednames;
    16.     public int[] loadedscores;
    17.  
    18.     private void Start()
    19.     {
    20.      
    21.         scoreDBscript = scoreDB.GetComponent<Scoreeditor>();
    22.         SaveNums = new int[5];
    23.         SaveNames = new string[5];
    24.     }
    25.     public void SaveData()
    26.     {
    27.         string path = Application.persistentDataPath + "BobSave.test";
    28.         FileStream stream = new FileStream(path, FileMode.Create);
    29.  
    30.         for(int i = 0; i <= 4; i++)
    31.         {
    32.             SaveNames[i] = scoreDBscript.HighScoreData[i].name;
    33.             SaveNums[i] = scoreDBscript.HighScoreData[i].number;
    34.         }
    35.  
    36.        // formatter.Serialize(stream, scoreDB.GetComponent<Scoreeditor>().HighScoreData);
    37.        // formatter.Serialize(stream, SaveNums);
    38.         stream.Close();
    39.         Debug.Log("successfully saved");
    40.  
    41.     }
    42.  
    43.     public List<ScoreClass> LoadData()
    44.     {
    45.         string path = Application.persistentDataPath + "BobSave.test";
    46.         if (File.Exists(path))
    47.         {
    48.             Debug.Log("the file exists");
    49.             FileStream stream = new FileStream(path, FileMode.Open);
    50.  
    51.             loadednames = new string[5];
    52.             loadedscores = new int[5];
    53.  
    54.      
    55.             List<ScoreClass> loadedList = new List<ScoreClass>();
    56.  
    57.          
    58.          
    59.             for(int i = 0; i <= 4; i++)
    60.             {
    61.                loadedscores[i] = (int)formatter.Deserialize(stream);
    62.                loadednames[i] = formatter.Deserialize(stream) as string;
    63.                loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    64.              
    65.             }
    66.             stream.Close();
    67.             return loadedList;
    68.  
    69.         }
    70.         else
    71.         {
    72.             Debug.LogError("save file not found in: " + path);
    73.             return null;
    74.         }
    75.     }
    76.  
    77. }
     
    Last edited: Mar 16, 2020
  10. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    i went to the save file location on my computer and it exists as it should but windows reports its 0KB, how is that possible? i saved it correctly didnt i?
     
  11. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  12. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    But are the contents of the file actually there?
     
  13. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  14. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  15. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Add more debugging. Verify what exactly you are saving to the file, verify you are getting your "successfully saved" message, verify you're getting 0 runtime errors, and verify you're looking at the correct file.
     
  16. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    i should be saving two arrays, one of strings and the other of numbers. What should i do to ensure theyre saving correctly? ive never worked with serializers before today.

    EDIT: sorry, i cant believe i didnt realize i'd commented out the actual saving earlier for debugging. The file saves data now. However loading brings up an InvalidCastException at this line:
    Code (CSharp):
    1. loadedscores[i] = (int)formatter.Deserialize(stream);
    commenting out that line leads to another error being discovered in this loop:
    Code (CSharp):
    1. for(int i = 0; i <= 4; i++)
    2.             {
    3.                //loadedscores[i] = (int)formatter.Deserialize(stream);
    4.                loadednames[i] = formatter.Deserialize(stream) as string;
    5.                loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    6.                
    7.             }
    at this line:
    Code (CSharp):
    1. loadedList[i] = new ScoreClass(loadedscores[i], loadednames[i]);
    "Arguement out of range exception, index was out of range. Must be non-negative and less than the size of the collection"
     
    Last edited: Mar 17, 2020
  17. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    i realized the reason its out of range is probably because lists work differently than arrays, and i have to manually add each entry to a list, so i edited it as such and also changed the int it creates the scoreClass with to a default value to see if it could at least Deserialize the array of names:
    Code (CSharp):
    1.  for(int i = 0; i <= 4; i++)
    2.             {
    3.                //loadedscores[i] = (int)formatter.Deserialize(stream);
    4.                loadednames[i] = formatter.Deserialize(stream) as string;
    5.                loadedList.Add(new ScoreClass(10, loadednames[i]));
    6.  
    7.             }
    and this is the error i get: https://i.gyazo.com/e21faceddc20840b3f497bd79a455f77.png
     
  18. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    oh my goodness i am so close to solving this i can feel it! i moved the assignment of Loadednames outside of the for loop, like so:
    Code (CSharp):
    1. loadednames = formatter.Deserialize(stream) as string[];
    2.             for(int i = 0; i <= 4; i++)
    3.             {
    4.                //loadedscores[i] = (int)formatter.Deserialize(stream);
    5.                //loadednames[i] = formatter.Deserialize(stream) as string;
    6.                loadedList.Add(new ScoreClass(10, loadednames[i]));
    7.              
    8.             }
    and the game now loads the saved names! Now i just need to figure out how to get my score ints as well! Any ideas?
     
  19. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    i got it to load the numbers! doing the same thing i did to loadednames to loadednumbers did the trick! i just need one last bit of help. as you can see from the full script above, all of this loading comes from a function called LoadData. Calling LoadData from a button with OnClick worked, as long as that button was set to load a different function on a different script which called it, "loadlist" however for some odd reason attempting to call loadlist in the start function of another script causes unity to say this: https://i.gyazo.com/807da8de91e0844df47f1776affc96f0.png the line its referring to is this one:
    Code (CSharp):
    1. HighScoreData = save.LoadData();
    but this makes no sense.
     
  20. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    what's a method to wait for the rest of the scene to load first? i think the problem is that calling loadlist from a start function makes it run too early before everything else is ready, because it works from any other method ive tried, except awake.
     
    Last edited: Mar 17, 2020