Saving and Loading Data Across Sessions using PlayerPrefs in Unity

The Singleton Pattern is one approach to maintaining data across scenes. It will create an object and then tell Unity not to destroy it when a new scene loads. However, the Singleton Pattern depends on a GameObject with the Script Component being created and then maintained across future scenes. It also does not save data across sessions. Closing the game destroys the data. When the game is re-opened, the data is reset. In order to maintain data across sessions, a different technique is need: PlayerPrefs.

In Unity, PlayerPrefs is a class with static methods. It can be accessed by any other class for the purpose of saving preferences in a more permanent manner. Internally, Unity saves files as part of a storage pattern based on two values in the Project Settings, company name and product name.

Adjusting and Confirming Project Settings

Setting Company Name and Product Name

After opening a project in the Unity editor, the company and product name can be changed through going to Edit -> Project Settings.

Selecting “Player” from the left-side options opens the Player Settings.

Player Settings

The “Company Name” and “Product Name” fields can be changed and set to new values.

Once changed, closing the window closes and saves any changes to the Player Settings under the Project Settings window.

Player Preferences Save Paths

Unity saves player preferences based on the company and name fields from the Player Settings. These are used, depending on the operating system, to create files containing the “preferences” of the player and retained, unless explicitly deleted by the user or system, across sessions.

Player Preferences Paths Per Operating System:

  • MacOS X: ~/Library/Preferences/com.ExampleCompanyName.ExampleProductName.plist
  • Windows: HKCU\Software\Unity\UnityEditor\ExampleCompanyName\ExampleProductName
  • Linux: ~/.config/unity3d/ExampleCompanyName/ExampleProductName

Working with PlayerPrefs

The class PlayerPrefs and its static methods can be accessed from any C# class (any Script Component). However, using player preferences follows a two-step process. Because the developer may not know if the user (or their system) has deleted the player preferences files, the first step should always be to check if a preference exists. If it does, it can be read from the files. If it does not exist, or if the developer is creating a value for the first time, it can be saved.

Understanding Dictionaries

Internally, Unity uses a Dictionary-based storage structure to save player preferences. In C#, a dictionary is based on the concept of looking up a word in a physical dictionary. Based on the word, a user can get its definition. The word is the key and its “definition” is its value. Through providing a key, a developer can get its value back.

PlayerPrefs has several built-in methods for checking values based on the type of its value. To make things easier to save, Unity uses three main types of data for values: int (whole numbers), float (decimal), and string (collections of letters, number, and other special symbols). For keys, Unity uses strings.

For example, the key “resolution-height” might be set to the number 800 and the “resolution-width” might be set to the number 600. In both cases, a string is used as the key and its value is either an int, float, or string.

Checking Preferences: HasKey()

At any moment, the user or their system could delete the player preferences files. With this in mind, the first step when working with the PlayerPrefs class should always be to check if a key exists before doing anything with it.

// Does the key "lives" exist?
//
// If it does not, two things might have happened:
// 1) Preferences files were recently deleted.
// 2) It was never created.
if(PlayerPrefs.HasKey("lives"))
{

} 

In a key exists within the Dictionary internally in the PlayerPrefs class, the method HasKey()will return true. This should always be the first step when working with values because of the uncertainty involved in working with the files. Always check to see if a value exists first before attempting to update or create it.

Saving Preferences

Unity allows three types of data to be saved as part of the player preference files: whole numbers (integers), decimal numbers (float), and collections of letters, numbers, and special symbols (strings). Each of these data types has its own method as part of the PlayerPrefs class.

// Does the key "lives" exist?
//
// If it does not, two things might have happened:
// 1) Preferences files were recently deleted.
// 2) It was never created.
if(PlayerPrefs.HasKey("lives"))
{
            
} 
else
{
 // (Assume this is the first time using this key 
 //  or it does not otherwise exist.)
 //
 // Set the "lives" to 5
 PlayerPrefs.SetInt("lives", 5);
}

Using any of the “set” methods saves the data in the memory of the application for later uses of the PlayerPrefs class methods to retrieve the value. To finally write the data to a file, one last method call is needed: Save().

// Does the key "lives" exist?
//
// If it does not, two things might have happened:
// 1) Preferences files were recently deleted.
// 2) It was never created.
if(PlayerPrefs.HasKey("lives"))
{
            
} 
else
{
    // (Assume this is the first time using this key 
    //  or it does not otherwise exist.)
    //
    // Set the "lives" to 5
    PlayerPrefs.SetInt("lives", 5);

    // Save the preference to an external file.
    PlayerPrefs.Save();
}

Note: Because the method Save() deals with files, it should not be called frequently. Instead, it should be used as part of a user “saving” their game or at other pre-determined “checkpoints” such as when a user closes a certain menu.

Loading Preferences

As the data saved to the player preferences is either integers, decimals, or string values, the matching loading methods uses the same naming scheme as the “set” methods. Instead of “set,” the loading methods are all named after the verb “get”:

// Does the key "lives" exist?
//
// If it does not, two things might have happened:
// 1) Preferences files were recently deleted.
// 2) It was never created.
if(PlayerPrefs.HasKey("lives"))
{
  // The key "lives" exists.
  int lives = PlayerPrefs.GetInt("lives");
} 
else
{
  // (Assume this is the first time using this key 
  //  or it does not otherwise exist.)
  //
  // Set the "lives" to 5
  PlayerPrefs.SetInt("lives", 5);

  // Save the preference to an external file
  PlayerPrefs.Save();
}

Deleting Keys

In the cases where a developer may want to delete a particular key or all of the existing keys (such as when a player resets a save file or switches back to default values), PlayerPerfs has two methods for this purpose.

Limits of PlayerPrefs

The PlayerPrefs class and its methods can be very useful for storing any data considered “preferences.” This might include the player’s choice of resolution, graphical settings, or other accessibility options. Anything the player would change as part of interacting with options within menus and changing to match their needs or wants when using the game or application.

It can be tempting to think the PlayerPrefs class is a great place for data like passwords, the player’s current save files, or other, more sensitive information. However, because the location of where Unity saves the files is known, this make it a very poor choice for this type of data. A user (or its system) can access and change the player preferences files whenever they want. This means, for example, if the player’s experience points and level in a role-playing game were saved in the files, they could be edited and easily changed. While the PlayerPrefs can be used for simple values, like in the above use of lives in the example code, it should never be considered a trusted source.

PlayerPrefs should not be used as a replacement for creating save files and other more complex data management for a game. It exists to save “preferences” and not complex data. This means any heavy usages of reading and writing to preference files may cause significant delays in performance. Unity has other tools for creating complex data files and handling generating save files for games outside of saving its “preferences.”