Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Help needed to create high score system (with saving and loading to binary format)

Discussion in 'Scripting' started by Masashi86, Oct 11, 2015.

  1. Masashi86

    Masashi86

    Joined:
    Jul 9, 2014
    Posts:
    7
    I'm creating a arcade-style shoot-em up game for Android(first one I'm aiming for publishing :) ), and I got little stuck with the high score system. I think I need little help. I'm trying to create a high score system that saves few different values from the end of the player's run, score, time & kills, and loads the previous records when player checks them from a menu, and maybe later on in the development, I could use that record system on online ranking system(online stuff is not yet important, though).

    I looked a sample tutorial for making the binary file saver/loader system and set it up, and it seems to work on saving and loading single values decently, but now I should try to save also to a list and load from the list (and sort them too by the best values). And here is where I'm not very sure what would be the best solution for this.
    How to set up a list or dictionary to this GameDataControl script so that I can access it from the menu system (and show the records of score, time & kills in their own UI text object)?

    Here's the base of the GameDataControl script I've started:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System;
    5. using System.IO;
    6. using System.Runtime.Serialization.Formatters.Binary;
    7.  
    8. public class GameDataControl : MonoBehaviour
    9. {
    10.     public static GameDataControl gameData;
    11.     //list of highscores
    12.     public List<int> scores = new List<int>();
    13.     // profile data
    14.     public string playerName;
    15.     public string shipType;
    16.     public int score;
    17.     public int playTime;
    18.     public int killCount;
    19.  
    20.     // Use this for initialization
    21.     void Awake ()
    22.     {
    23.         if(gameData == null) // if Game Data Controller is not in the scene, set this game object not to destroy on load
    24.         {      
    25.             DontDestroyOnLoad (gameObject);
    26.             gameData = this;
    27.         }else if(gameData != this) // if the scene already has a game object, as a GDC, destroy this object
    28.         {
    29.             Destroy (gameObject);
    30.         }
    31.    
    32.     }
    33.  
    34.     public void AddToScoreList()
    35.     {
    36.         // the logic to save the high score on the list
    37.         scores.Add (score);
    38.     }
    39.     public void SaveData()
    40.     {
    41.         // creating the binary file
    42.         BinaryFormatter bf = new BinaryFormatter();
    43.         FileStream file = File.Create(Application.persistentDataPath + "/playerProfileData.dat");
    44.         // get the data to save
    45.         PlayerProfileData playerData = new PlayerProfileData();
    46.         // set the data to save in the file
    47.         playerData.playerName = playerName;
    48.         playerData.shipType = shipType;
    49.         playerData.score = score;
    50.         playerData.playTime = playTime;
    51.         playerData.killCount = killCount;
    52.         playerData.highScores = scores;
    53.  
    54.         bf.Serialize (file, playerData);
    55.         file.Close ();
    56.     }
    57.  
    58.     public void LoadData()
    59.     {
    60.         if(File.Exists(Application.persistentDataPath + "/playerProfileData.dat"))
    61.         {
    62.             // open the binary file, and set the loaded data
    63.             BinaryFormatter bf = new BinaryFormatter();
    64.             FileStream file = File.Open(Application.persistentDataPath + "/playerProfileData.dat",FileMode.Open);
    65.             PlayerProfileData playerData = (PlayerProfileData)bf.Deserialize(file);
    66.             // change current data to loaded data
    67.             playerName = playerData.playerName;
    68.             shipType = playerData.shipType;
    69.             score = playerData.score;
    70.             playTime = playerData.playTime;
    71.             killCount = playerData.killCount;
    72.             scores = playerData.highScores;
    73.             file.Close ();
    74.  
    75.         }
    76.     }
    77.  
    78. }
    79.  
    80. [Serializable]
    81. class PlayerProfileData
    82. {
    83.     public List<int> highScores;
    84.     // profile data
    85.     public string playerName;
    86.     //stored player stats
    87.     public string shipType;
    88.     public int score;
    89.     public int playTime;
    90.     public int killCount;  
    91.  
    92. }
    93.  
    94.  
    What methods or functions I could use either in this same script or calling from other scripts, that could work on this?
    I'm thinking also, that there could be a button for debugging that resets the data on the binary file, so if you have any suggestions for this too; it would help me a lot.
     
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    For a small set of local data, Use PlayerPrefs
     
  3. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I am thrown by the binary aspect. Not sure why that is so important in this case. Why not just store the data as text?
    StreamWriter would do the trick and you could just use ReadLine and WriteLine to save/fetch one record at a time.

    alternatively, simply add a ToString() function to your class that concatenates all your fields by '|' then concatenate all your objects by '\n' and save the final string using PlayPrefs...

    Code (csharp):
    1. void SaveAll()
    2. {
    3. string results = string.Empty;
    4. foreach( PlayerProfileData rec in AllRecords) results += rec.ToString() + '\n';
    5. PlayerPrefs.SetString("Scores", results);
    6. }
    7.  
    8. void LoadAll()
    9. {
    10. string[] all_scores = PlayerPrefs.GetString("Scores", string.Empty).Split('\n');
    11. AllRecords = new PlayerProfileData[all_scores.length-1];
    12. for( int i = 0; i < AllRecords.Length; i++)
    13.     AllRecords[i].ParseData(all_scores[i]);
    14. }
    Ta daa... But if binary is important to you then I can't help, sorry... :(
     
  4. Masashi86

    Masashi86

    Joined:
    Jul 9, 2014
    Posts:
    7
    Thanks for replies!
    Well, I could try that playerPrefs too, I need just to learn how to use it. If I use the playerPrefs, can I save dictionaries there? If so, how it could be made?

    Because, later I'm adding an achievement system and "Enemy Encyclopedia"-thingie that records what types of enemies player has encountered, and shows a bit info about the enemy. These all should be called when player checks the record from the menu, and so all these data should be added to somewhere to save them and load them when needed.
    I'm thinking that dictionaries might be a good solution for both Achievement and Encyclopedia system, because you can make the key as a string, or by the index integer(and I guess other types as well), and return the type you're searching(like a gameObject).
    I'm just a bit unsure how to use them with the saving loading system. If I could learn to do this with playerprefs, it might help me find a good solution for the binary formatting too. I guess the biggest difference is how to handle the IO data.
    I chose the binary formatting, because it was said that it would be more safe way (especially if in the future there would be public rankings online).

    This project's one point is also to learn to code(and use the code to a good goal) and how to do things in Unity, so if people happens to like the game I'm creating, I could use this knowledge to next projects (and do them bit more professionally step by step).
    So all different of ways to find a solution for this are welcome.
     
  5. Masashi86

    Masashi86

    Joined:
    Jul 9, 2014
    Posts:
    7
    Ahem, I was bit of a dumba** here :D
    I noticed that when I was calling the save/loader in the Update, not in a single function call, and it was messing the system well (it was just checking if bool gameover was true, setting and adding the high score every frame to the list). This seemed to be the source of the issue. Now the saving and loading the list seems to work ok. It's not yet perfect, and I need to do some research about the dictionaries more and how they could be utilized here. But I think I ask about them in separate thread if I can't find answers myself.
    Thanks anyway!
     
  6. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Sorry for doing this but I'm gonna advertise myself here... Go onto the Asset Store and look for MBS Core (myBad Studios core). It will drastically simplify your life. I recently had to create a monster dictionary and it took me somewhere in the region of 5 minutes to do.

    First you will need a database that stores all the info about the monster (can be created by hand or using the kit but by hand is so much faster) and then you just need to store which ones you have. Once you know which ones you have you can show all the details you like of only the monsters you have...

    Example:
    Code (csharp):
    1. <Monsters>
    2. <Pikmin>size=3; difficulty=easy; color=red; gender:other
    3. <Peach>size=1; difficulty=baby easy; color=pink; gender:female;
    4. <MetalSonic>size=9; difficulty=Rockstar; color=gray; gender:none;
    That would be your database stored as Assets/Resources/monsters.txt.

    Now, Step 1: when you start your game you do this in file Monsters.cs:
    static CML monsters = new CML("monsters");
    and this in MySaveGames.cs:
    static CML savegames = new CML("savegames");

    Step 2: When you play against MetalSonic you do this:
    MySavedGames.savegames.Seti("Found",1,"MetalSonic");
    MySavedGames.savegames.Save("savegames");

    Step3: When you want to show your monster data, first get a list of all your found monsters:
    cmlData found = MySavedGames.savegames.FirstNodeOfType("Found");
    cmlData selected =null;

    Then, you let the player select which monster to view:
    Code (csharp):
    1. foreach (string s in found.Keys)
    2. if (GUILayout.Button(s))
    3. selected = Monsters.monsters.FirstNodeOfType(s));
    ...and finally, show whatever data you want:
    Code (csharp):
    1. string valueToShow = "<color "+ selected.String("color") +">Name:" + selected.data_type + " size: " + selected.Float("size") + " gender:" + selected.String("gender") + " difficulty:" + selected.String("difficulty") + "</color>";
    There is a hell of a lot more you can do but for a monster dictionary this took me almost no time at all... might be worth a look...