Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Saving an unlocked character

Discussion in 'Scripting' started by sebwin555, Oct 8, 2018.

  1. sebwin555

    sebwin555

    Joined:
    Sep 9, 2018
    Posts:
    22
    Hey guys!
    I'm relativley new to Unity and I have a problem with the saving of an unlocked character! So basically I have a shop scene where you can unlock new characters with your coins/money, but when you unlock/buy something (which works fine) and then you stop the inspector and start again you would have to buy this character again, because I don't know how to save it!

    Here are my scripts and I think that I should add a line in the "BuysScript2" in line 35, because that's where I unlock the character!

    Thanks for your help!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public static class globalcurrency  {
    6.  
    7.     public static int character;
    8.     public static bool character2unlocked;
    9. }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class BuyScript : MonoBehaviour {
    7.  
    8.     public int myChar;
    9.     private float price;
    10.  
    11.     public Text coinss;
    12.     public Text pricetext;
    13.  
    14.  
    15.  
    16.     void Start() {
    17.         price = GetComponent<BuyScript2> ().price;
    18.  
    19.     }
    20.  
    21.     void Update(){
    22.  
    23.         if (myChar == 1)
    24.         {
    25.             if (globalcurrency.character == 1)
    26.             {
    27.                 pricetext.text = "selected";
    28.             }
    29.             else
    30.             {
    31.                 pricetext.text = "select";
    32.             }
    33.         }
    34.  
    35.         if (myChar == 2)
    36.         {
    37.             if (globalcurrency.character2unlocked)
    38.             {
    39.                 if (globalcurrency.character == 2)
    40.                 {
    41.                     pricetext.text = "selected";
    42.                 }
    43.                 else
    44.                 {
    45.                     pricetext.text = "select";
    46.                 }
    47.             }
    48.             else
    49.             {
    50.                 pricetext.text = price + "x" ;
    51.             }
    52.         }
    53.         coinss.text =  ((int)PlayerPrefs.GetInt ("Coins")).ToString () + "x";
    54.     }
    55.  
    56.  
    57.  
    58.  
    59.  
    60. }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BuyScript2 : MonoBehaviour {
    6.  
    7.     private int myChar;
    8.     public int price;
    9.     private int money;
    10.  
    11.  
    12.     // Use this for initialization
    13.     void Awake () {
    14.         myChar = GetComponent<BuyScript> ().myChar;
    15.         money = PlayerPrefs.GetInt ("Coins");
    16.  
    17.     }
    18.    
    19.     // Update is called once per frame
    20.     void Update () {
    21.  
    22.         if (myChar == 1) {
    23.             globalcurrency.character = 1;
    24.  
    25.         }
    26.  
    27.         if (myChar == 2) {
    28.             if (globalcurrency.character2unlocked) {
    29.                 globalcurrency.character = 2;
    30.             } else {
    31.                 if (money >= price) {
    32.                     PlayerPrefs.SetInt("Coins", money - price);
    33.                     PlayerPrefs.Save();
    34.                     globalcurrency.character2unlocked = true;
    35.  
    36.                 }
    37.             }
    38.         }
    39.  
    40.         GetComponent<BuyScript2> ().enabled = false;
    41.     }
    42.  
    43. }
     
  2. Serinx

    Serinx

    Joined:
    Mar 31, 2014
    Posts:
    785
    There are many potential ways to save data. You've already got one in your script (PlayerPrefs).
    Although the simplest method, it's not very secure because players can easily modify these values to cheat by giving themselves coins.

    Data serialisation is another way of doing it by saving specific information to a local file. This is a bit more secure, gives you more control, and allows you to define your own structures rather than relying on playerprefs.
    You can learn more about this from this Unity tutorial (or search youtube).
    https://unity3d.com/learn/tutorials/topics/scripting/persistence-saving-and-loading-data

    The most secure way (but also the most complex) is to save the values to a database.
    If you want a super secure way of storing your users progress, this is the way to go, but it will take a lot of research.
    There's plenty of tutorials on youtube for how to achieve this if you feel like going the extra mile:


    Then again, if you're just doing it for fun and don't care about cheating, just use PlayerPrefs to store your unlocks too.

    You could use an Integer to store whether the item is unlocked or not e.g. 0 = locked and 1 = unlocked.

    //Unlock a character:
    PlayerPrefs.SetInt("Char2Unlocked", 1);

    //Check if a character is unlocked
    globalcurrency.character2unlocked = PlayerPrefs.GetInt("Char2Unlocked") = 1;
     
    sebwin555 likes this.
  3. Gunging

    Gunging

    Joined:
    Sep 6, 2016
    Posts:
    139
    I'd try the Serialization, it is very easy and secure enough for beginners:

    Create a whole new script, make it look like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.IO;
    4. using System.Runtime.Serialization.Formatters.Binary;
    5.  
    6. public class PersistentData : MonoBehaviour {
    7.  
    8.     void Start() {
    9.         //The first (and only) thing this script does as a component, is Load the saved data
    10.         Load();
    11.     }
    12.  
    13.     //Creates a new save file, or overwrites the previous save file
    14.     public static void Save() {
    15.  
    16.         //Starts the File Stream
    17.         BinaryFormatter binaryFormatter = new BinaryFormatter();
    18.         //Creates a new file (overwriting the previous one)
    19.         FileStream fileStream = File.Create(Application.persistentDataPath + "/playerData.dat");
    20.        
    21.  
    22.  
    23.         ///Stores all values, so they can be saved
    24.         PlayerData pD = new PlayerData();
    25.  
    26.         //The 'character' value in the new file is set to whatever the value in 'globalcurrency'
    27.         pD.character = globalcurrency.character;
    28.  
    29.         //The 'character2unlocked' value in the new file is set to whatever the value in 'globalcurrency'
    30.         pD.character2unlocked = globalcurrency.character2unlocked;
    31.  
    32.  
    33.         ///Wraps up the new file, and saves it
    34.         binaryFormatter.Serialize(fileStream, pD);
    35.  
    36.         //Finishes the File Stream
    37.         fileStream.Close();
    38.     }
    39.  
    40.     public static void Load() {
    41.         //First of all, checks if the file exists. If it doesn't exist, nothing happens because there is nothing to Load.
    42.         if (File.Exists(Application.persistentDataPath + "/playerData.dat")) {
    43.  
    44.             //The file does exist, then, the File Stream is started
    45.             BinaryFormatter binaryFormatter = new BinaryFormatter();
    46.            
    47.             //The saved file is opened
    48.             FileStream fileStream = File.Open(Application.persistentDataPath + "/playerData.dat", FileMode.Open);
    49.  
    50.             //The opened file is interpreted as a 'PlayerData' class
    51.             PlayerData pD = (PlayerData)binaryFormatter.Deserialize(fileStream);
    52.  
    53.             //The File Stream is closed, because we already opened the file and got all the values from it.
    54.             //All the values of it are now in the variable 'pD' by the way.
    55.             fileStream.Close();
    56.  
    57.             ///Loads all Values
    58.             //The value of 'character' in globalcurrency is set to whatever was Loaded
    59.             globalcurrency.character = pD.character;
    60.             //The value of 'character2unlocked' in globalcurrency is set to whatever was Loaded
    61.             globalcurrency.character2unlocked = pD.character2unlocked;
    62.         }
    63.     }
    64. }
    65.  
    66. [Serializable] class PlayerData {
    67.     public int character;
    68.     public bool character2unlocked;
    69. }
    70.  

    Once you can save and load, you have to make it happen. So search every time the value 'globalcurrency.character' is modified (
    globalcurrency.character = 3
    for example) and right after it, add the line "
    PersistentData.Save();
    ." The same for every time the value 'globalcurrency.character2unlocked' is modified.

    Then, if your new code looks like this:
    Code (CSharp):
    1.         if (myChar == 1) {
    2.             globalcurrency.character = 1;
    3.             PersistentData.Save(); //Saves the changes
    4.         }
    5.         if (myChar == 2) {
    6.             if (globalcurrency.character2unlocked) {
    7.                 globalcurrency.character = 2;
    8.                 PersistentData.Save(); //Saves the changes
    9.             } else {
    10.                 if (money >= price) {
    11.                     PlayerPrefs.SetInt("Coins", money - price);
    12.                     PlayerPrefs.Save();
    13.                     globalcurrency.character2unlocked = true;
    14.                     PersistentData.Save(); //Saves the changes
    15.                 }
    16.             }
    17.         }
    That was the Saving part, now we have to Load it whenever the game starts. Thus, in the starting scene of your game, create an empty gameobject and attach a PersistentData component to it. Whenever the game starts, it will Load automatically. You might want to modify the execution order of PersistentData so it always runs first of everything.

    You select any script in the inspector, and in the top-right corner you will find the button:
    upload_2018-10-7_23-16-10.png
    You cick that little "+" sign, and search for "PersistentData".
    You just assign PersistentData a negative number and voila.

    Great! Now you can save and load. I'd recommend making the coins harder to cheat tho.

    There is an easy way of making there be multiple save files by editing a few things in PersistentData, but its late rn so I'm not going to explain how today, and wont at all unless someone asks.
     
  4. sebwin555

    sebwin555

    Joined:
    Sep 9, 2018
    Posts:
    22
    Thanks! I will try it out as soon as possible!

    And I know it's easy to cheat with Playerprefs, but do you think that so many people are going to do that or do even know about it?
     
  5. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,093
    Yes. Unity has had it since the very beginning and it has been used in a huge number of games because it's very easy for a beginner to use. Additionally the data is stored in a very easy to access location in a format that is trivial to edit.

    https://gamedev.stackexchange.com/q...-to-store-inventory-and-scores-in-playerprefs
     
    Last edited: Oct 8, 2018
  6. sebwin555

    sebwin555

    Joined:
    Sep 9, 2018
    Posts:
    22
    It's working now and it does save which one is selected, but how can I now unlock the character in my game scene? I tried that I just made two script with two methods (one gameobject.setactive(true) and one with gameobject.setactive(false)) and then I added in my buyscript2 that when the player selects the first character it calls at the first character the method setactive(true) and at the the second character it simply does setactive(false), but it won't work that way :(
     
  7. sebwin555

    sebwin555

    Joined:
    Sep 9, 2018
    Posts:
    22
    so this is the script where I do setactive and I added it to my first character in the game scene
    Code (CSharp):
    1. public class tooglecar1 : MonoBehaviour {
    2.  
    3.     public static tooglecar1 Instance { set; get; }
    4.  
    5.     public void SetActive ()
    6.     {
    7.         gameObject.SetActive (true);
    8.     }
    9.  
    10.     public void Disabled ()
    11.     {
    12.         gameObject.SetActive (false);
    13.     }
    14. }
    the other script looks the same just with a different name!

    and then I thought I could simply add line 24,25 and 32, 33
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BuyScript2 : MonoBehaviour {
    6.  
    7.     private int myChar;
    8.     public int price;
    9.     private int money;
    10.  
    11.  
    12.     // Use this for initialization
    13.     void Awake () {
    14.         myChar = GetComponent<BuyScript> ().myChar;
    15.         money = PlayerPrefs.GetInt ("63952");
    16.  
    17.     }
    18.  
    19.     // Update is called once per frame
    20.     void Update () {
    21.  
    22.         if (myChar == 1) {
    23.             globalcurrency.character = 1;
    24.             tooglecar1.Instance.SetActive ();
    25.             tooglecar2.Instance.Disabled ();
    26.             PersistentData.Save ();
    27.         }
    28.  
    29.         if (myChar == 2) {
    30.             if (globalcurrency.character2unlocked) {
     
  8. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    That's because in your code you updated playerprefs but you never updated the money variable.
     
  9. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    Your scripts are confusing and contain unnecessary redundant copies of your money/highscore. If you're going to store the money in a global variable anyway, why do you have a separate local variable to store it in each of your other two classes?

    I'm thinking that you want to update the highscore in your save manager because that is what drives the HighScore text display?
     
    ArshidaGames likes this.
  10. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    I'm not sure yet. Why is it called the save manager? Right now it doesn't save anything.

    Anyway, the quick fix would be: In your BuyScript2, under the line of code that says:
    PlayerPrefs.SetInt("Highscores", money - price);

    add
    SaveManager.Instance.HighScore = money-price;



    Or, actually, maybe you'd rather do
    GlobalValues.money = money-price; 
     
    ArshidaGames likes this.
  11. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    If you want to use this persistent data thing, than I would probably remove any use of playerprefs in your BuyScript2. You probably want to get rid of the money variable too and use GlobalValues.money instead.
     
    ArshidaGames likes this.
  12. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    Oh, I just noticed that your persistent data is not saving the money or the highscore. just everything else. I thought you were saving everything in the GlobalValues. Did you mean to add these to the persistent data class?
     
    ArshidaGames likes this.
  13. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    So add those to your persistant data class. Then that should replace wherever you were saving and loading these to playerprefs before.
     
    ArshidaGames likes this.
  14. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    It's all the same as the other items you already have in there. You should be able to look at what's already there and figure out how to add the new ones.
    In your player data, add members to store those values:
    Code (csharp):
    1.  
    2. public int money;
    3. public int highscore;
    4.  
    In your save function where you already have:
    Code (csharp):
    1.  
    2. pD.character5unlocked = GlobalValues.character5Unlocked;
    3.  
    just add
    Code (csharp):
    1.  
    2. pD.money = GlobalValues.money;
    3. pD.highscore = GlobalValues.highscore;
    4.  
    Do the same thing in your Load() function.

    Basically, your just adding money and highscore to the list that's already there.
     
    ArshidaGames likes this.
  15. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    But didn't you already have code that did that? All you have to do is change it to use persistentData instead of PlayerPrefs.
     
    ArshidaGames likes this.
  16. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    It looks like it mostly works. You just need to subtract the money, right?

    Code (csharp):
    1.  
    2.             if (money >= price)
    3.                 {
    4.                  
    5.  
    6.                     GlobalValues.character2Unlocked = true;
    7.                     PersistentData.Save();
    8.                 }
    9.  
    So right before you call Save(), just add:
    Code (csharp):
    1.  
    2. GlobalValues.money = GlobalValues.money-price;
    3.  
     
    ArshidaGames likes this.
  17. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    I don't know what happens before the code that you listed. Are you sure that myChar==2 is ever true? Use Debug.Log to see the value of variables in the console as the program is running.

    As for highscore, are you actually looking at globalValues.highscore or are you looking at SaveManager.Instance.HighScore? Did you ever actually set the globalValues.highscore to a value in the first place?
     
    ArshidaGames likes this.
  18. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    Currently your save manager is only ever going to show 0 for highscore or currentscore. Maybe you want to set these text fields to show globalValues.highscore and globalValues.money instead?

    When do you want highscore to change?
     
    ArshidaGames likes this.
  19. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    I guess you need to set the highscore at the end of each game then?
     
    ArshidaGames likes this.
  20. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    By the way, in BuyScript2, this line in Awake probably doesn't do what you think it does:
    myChar = GetComponent<BuyScript>().myChar;


    This copies the value from BuyScript.myChar into the local myChar once on Awake. If you change BuyScript.myChar at any other time, the value of BuyScript2.myChar will not change.

    Why are BuyScript and BuyScript2 separate anyway?
     
    ArshidaGames likes this.
  21. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    Did you try adding PersistentData.Save() there?
     
    ArshidaGames likes this.
  22. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,335
    Actually On your onTriggerEnter2D you are updating savemanager.highscore. Did you mean to update globalValues.highscore there instead?
     
    Deleted User and ArshidaGames like this.
  23. Deleted User

    Deleted User

    Guest

    Hello, i am curious to know if you read this and can asnwer me back please?
     
  24. Deleted User

    Deleted User

    Guest

    Hi my firend, are you there? i hope i hear from you back. the best wishes for you!