Writing PlayerPrefs, Fast
An other way of saving data in Unity3D in a platform-independent way is to use the PlayerPrefs class.
This class works like a hash table, allowing you to store key-value pairs.
The disadvantage of the built-in PlayerPrefs class is that it’s really slow on iOS and even slower on Android. During a test we did on a Google Nexus One, we saved only 6.25 key-value pairs per second. Because we were trying to save over 300 records, this took way too long. Most likely, they are doing file I/O operations for every modification. The current PlayerPrefs class is clearly not designed to handle a larger amount of data.
So we decided to implement our own version of the PlayerPrefs class, allowing faster saving of tuning parameters in our prototypes. Now we’re saving 300+ records almost instantly on on the same device.
If you’re already using the Unity PlayerPrefs class in your project, and you’re looking for an optimization on iOS and Android devices, you can do the following:
- Download our PlayerPrefs.cs file and add it to your project.
- Add the following line at the beginning of the files in which PlayerPrefs is used:
using PlayerPrefs = PreviewLabs.PlayerPrefs;
- Use PlayerPrefs.Flush() to save all values. This can be done after writing a bunch of data, or when the application is quit (in your main game class):
public void OnApplicationQuit() { PlayerPrefs.Flush(); }
To obtain this optimization, we’re keeping a Hashtable and only writing it to a file when Flush() is called.
We’re deliberately not using an XML format to do this, as this would create an overhead unwanted on these lower-end devices.
masashi wada said:
Mar 12, 11 at 8:31 pmHi , nice work and nice code.
I found the problem on which the PlayerPrefs does not operate by android.
And I found your great code by the forum.
However, your code also hit the one problem.
InvalidCastException occurred in GetInt() after Deserialize().
In Deserialize(), this had added values to the string type hash table.
thanks.
JakeL168 said:
Mar 13, 11 at 10:30 pmHey, I downloaded your code, and unity get an error saying: “‘UnityEngine.Application’ does not contain a definition for ‘persistentDataPath’” at line 28
JakeL168 said:
Mar 13, 11 at 10:46 pmproblem solved… thanks for the code… Can I really use it for free?
Bernard François said:
Mar 13, 11 at 11:55 pmThe code is really for free. It has been released in the public domain and thus freed from copyright.
You can use it in any way you want!
The persistentDataPath member may not be available or working correctly for Unity versions prior to 3.3. Make sure you update your Unity version, or modify the code so it uses the correct file path. More explanation can be read in this blog post.
Also, please note that this code will not work for the web version. If you’re working on a game that should run both in the web player and on other platforms, you’ll have to use the following code in the beginning of the files where you want to use PlayerPrefs:
Richard said:
Mar 15, 11 at 2:06 pmIs there anyway I can call the CS script from my js files. I only use js.
Bernard François said:
Mar 15, 11 at 3:48 pmRichard, to use this in JavaScript you have to put the PlayerPrefs.cs file in a folder named ‘Plugins’ in the Assets folder (you may have to create this). Then, in the beginning of your files using PlayerPrefs you have to put the following:
You’ll also have to modify the code so you’re making calls to PreviewLabs.PlayerPrefs instead of PlayerPrefs.
Bernard François said:
Mar 15, 11 at 5:33 pmAn updated PlayerPrefs.cs file has been uploaded.
@masashi: This should fix the issue you encountered.
masashi wada said:
Mar 19, 11 at 8:52 pmHi Bernard!
Thanks for sounds good message.
Your new code worked by android devices.
I thank your work and wish to use for my products.
JJ said:
Apr 14, 11 at 5:07 amVery cool, great to see someone take this on.
I am a bit confused as to where the file is stored. I looked in the regular player prefs location on my Mac and the only file I have there for my game has no keys beyond Graphics Quality. Any hints?
Bernard François said:
Apr 14, 11 at 7:05 amJJ, the player prefs of this class are stored on the following location:
If you add the following line in your code (e.g. in a Start() method), you’ll now where it is exactly, regardless of the platform you’re running it on:
Burlak said:
Jul 09, 11 at 4:01 pmThanks for the great code.
But it seems that when you update the application on Android stored data is lost.
Bernard François said:
Jul 10, 11 at 8:13 amThanks, Burlak. What do you mean exactly by ‘when you update the application on Android’?
rain rain said:
Aug 31, 11 at 4:04 pmhow do i implement it into my scene?
Bernard François said:
Sep 15, 11 at 8:58 am@rain rain: You’ll have to add it to the Assets folder of your project. To use it, you need to call the script from your source code. Please note that this only makes sense if you’re already using (or intending to use) the PlayerPrefs class – as the PreviewLabs.PlayerPrefs class is a faster version replacing it. If you’re not using PlayerPrefs to store data, there will be no optimization.
Lexxx said:
Nov 16, 11 at 6:33 amGreat! Thanks a lot!
It works on iPhone/iPod ?
Bernard François said:
Nov 16, 11 at 8:00 amYes, it does work on iOS and is actually a lot faster than the built-in PlayerPrefs – especially when storing a lot of data.
Sebastian said:
Jan 15, 12 at 12:24 amPeeerfect!
For hours I thought I had a problem with my code, but then I tried this and found out it was just a bug with Unity’s PlayerPrefs >_< Thanks a bunch
Clark said:
Jan 28, 12 at 3:29 amJust wanted to note, we had an issue with this where the Deserialize() function might crash the game on iOS/Android devices if the PlayerPrefs file exists, but doesn’t have anything in it. We made the following changes to make it safer:
if(serializedInput != null)
{
string[] parameters = serializedInput.Split(new string[] {” ” + PARAMETERS_SEPERATOR + ” “}, StringSplitOptions.None);
foreach(string parameter in parameters)
{
string[] parameterContent = parameter.Split(new string[]{” ” + KEY_VALUE_SEPERATOR + ” “}, StringSplitOptions.None);
if(parameterContent.Length == 3)
{
playerPrefsHashtable.Add(DeEscapeNonSeperators(parameterContent[0]), GetTypeValue(parameterContent[2], DeEscapeNonSeperators(parameterContent[1])));
}
else
{
Debug.LogWarning(“PlayerPrefs::Deserialize() parameterContent has ” + parameterContent.Length + ” elements”);
}
}
}
Joshery said:
Mar 15, 12 at 9:45 pmMay need to add the following to the DeleteAll() function if you haven’t changed anything else in the hash table:
hashTableChanged = true;
Also, need to call Flush() after calling DeleteAll()