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. Dismiss Notice

Question Good use for PlayerPrefs?

Discussion in 'Scripting' started by Wigglepuff, Jun 20, 2023.

  1. Wigglepuff

    Wigglepuff

    Joined:
    Dec 28, 2021
    Posts:
    3
    Hi all, I've started using PlayerPrefs to help transition data between my scenes. However, after looking around the internet for more info, I've seen a lot of people say that PlayerPrefs are terrible for things related to game data, and should only be used for things like settings.

    Currently, I'm using PlayerPrefs to remember what characters I have selected. My game is a prototype of a rouge-like game (think "Into The Breach" related), so the characters are the same until the end of the run. The way it works is:

    First scene is a character selection menu, where I can select 3 characters. Each character has a unique integer attached to them. When I'm done selecting each character integer is saved to PlayerPrefs. Then, whenever I load into a new level, I grab the PlayerPrefs and use whatever integer they may be to spawn in my characters.

    Is this an effective way to do this? I'm not sure as to why this may be a bad thing, or what a better solution would be. Thanks in advance!
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Yes. By definition, if something gives you the desired result it is considered "effective". ;)

    Perhaps you meant "efficient"? In that case you'd have to elaborate on where you see it being inefficient. It will certainly not scale, and on some platforms PlayerPrefs (all settings of the same app) is limited in size (1 MB). But in your case, the selected character is nothing but a simple setting. So you should be okay to continue using it.
     
    Bunny83 likes this.
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,140
    A lot of times playerprefs are used to save large amounts of data, which isn't always the best use for them. But, generally speaking, it's better to use some sort of save system for that. Like, writing to a json file.

    But, for simple things, using playerprefs should be fine. What I would suggest is have a single script that you use to save information and load information, that way down the road if you want to use something else, you only have to edit the one script to save/load a different way.
     
    Bunny83 likes this.
  4. StarBornMoonBeam

    StarBornMoonBeam

    Joined:
    Mar 26, 2023
    Posts:
    209
    An important limitation to player prefs is there is an inability to transfer the save data. It is specific only to your machine. So players can't share save files. There is no file. It's scattered info. (Maybe you can with a sort of hack, but there are better ways). Depending solely on the player pref would mean you can't receive a save file that contains a bug if it was saved using Prefs. Or you can't share files between players. Sometimes that may not be necessary. All in all Player Prefs is just a mechanism to store something and retrieve something. Though yes you could store your games player progress this way, again it is very limited beyond that.

    It's most useful in most projects in a broad sense for overwriting default values. Like options. Key bindings etc. Current resolution etc.
     
    Ryiah likes this.
  5. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,082
    Cloud backup services provided by many platforms and stores won't function either.
     
    StarBornMoonBeam likes this.
  6. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    The main reason people say playerprefs is a bad place to save game state is because of its security.

    In that there is none. Its literally just a plain text file on the players PC when they are playing your game. They can edit it in real-time and change your gamestate to something they prefer right before you try to load it.

    The purpose of PlayerPrefs is in the name. Its meant to be a config file of a player's preferences so they can hot-edit their own settings for things you'd want to be in configuration files. like volume levels, screen resolutions, key bindings, etc. It was not meant for storing gamestate progression despite it more than being capable to.
     
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    People already suggested reasons for why PlayerPrefs should primarily used for "preferences" or "settings".

    ...

    As to your specific use. Everyone is talking about saves, and that's not even what you're doing though.

    You seem to be using PlayerPrefs as a place to cache data between scene transitions.

    But like... why?

    PlayerPrefs requires a disk write, where as you're just trying to store some minor string/int data between scenes. Simplest way to do that would just be have a static class with some static vars.

    Code (csharp):
    1. public static class GameState
    2. {
    3.     public static int SelectedPlayer;
    4. }
    Stick your int there and it'll be accessible come next scene.

    PlayerPrefs is helpful for storing data between play sessions (when someone closes your game and launches it again later). Things you might store that are "preferences/settings" are like if they are fullscreen or not, resolution choice, volume, etc.
     
    Bunny83, spiney199 and Ryiah like this.
  8. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,140
    I think we're talking about saves because even in rogue like games, there may be checkpoints where the player can quit the game and then come back to it in a future session, which means they want to maintain their character choice, equipement found in the run, etc. It's unclear what OP plans to do in the future. But, I agree, if none of those features exist, a static variable is a simple approach.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,560
    Don't do that. Just use a GameManager construct. Here's some ideas:

    ULTRA-simple static solution to a GameManager:

    https://forum.unity.com/threads/i-need-to-save-the-score-when-the-scene-resets.1168766/#post-7488068

    https://gist.github.com/kurtdekker/50faa0d78cd978375b2fe465d55b282b

    OR for a more-complex "lives as a MonoBehaviour or ScriptableObject" solution...

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance!

    The above solutions can be modified to additively load a scene instead, BUT scenes do not load until end of frame, which means your static factory cannot return the instance that will be in the to-be-loaded scene. This is a minor limitation that is simple to work around.

    If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

    Code (csharp):
    1. public void DestroyThyself()
    2. {
    3.    Destroy(gameObject);
    4.    Instance = null;    // because destroy doesn't happen until end of frame
    5. }
    There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

    OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

    And finally there's always just a simple "static locator" pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

    WARNING: this does NOT control their uniqueness.

    WARNING: this does NOT control their lifecycle.

    Code (csharp):
    1. public static MyClass Instance { get; private set; }
    2.  
    3. void OnEnable()
    4. {
    5.   Instance = this;
    6. }
    7. void OnDisable()
    8. {
    9.   Instance = null;     // keep everybody honest when we're not around
    10. }
    Anyone can get at it via
    MyClass.Instance.
    , but only while it exists.
     
    spiney199 likes this.