Trying desperately to save PlayerPrefs float value

Greetings. It’s been a while when I had visited this forum :slight_smile:

I’ve been struggling with this strange behaviour for two days and jumping between two AI to get it sorted out with no success.

Somehow I just can’t save float values with PlayerPrefs. Everytime when I check through Regedit.
There’s only a error on saved data: Invalid DWORD (32-bit) value with a garbage in value.

But! When I save int or string value it works flawlessly.

Would it be possible because i’m running originally Windows 11 23H2 english version and added
locales for finnish?

Read about using CultureInfo.InvariantCulture because some countries use period instead of comma in fractions. As we know; “Unity handles the conversion of the float value internally and ensures that it’s saved in a platform-independent manner. You don’t need to worry about formatting or culture-specific issues when saving floats to PlayerPrefs directly.” ← Sounds good on paper…

I was using Unity 2022.2.20f so I decided to update to 2022.3.19f if that helps.
Nope. Same behaviour continues.

I wouldn’t want to save certain floats from sliders to string and convert them somehow back to float for
actually use of those values.

Those values are being used in Options menu with sliders for adjusting brightness, music volume and fx volume.

Should I just forget using floats for more accurate results and set things to work with int? :frowning:

I’ll post those three scripts what I used when debugging this problem:

First is for saving string:

using UnityEngine;

public class PlayerPrefsStringExample : MonoBehaviour
{
    private const string stringTestKey = "StringTest";

    void Start()
    {
        // Save the string "This is A test!" to PlayerPrefs
        SaveStringToPlayerPrefs(stringTestKey, "This is A test!");

        // Retrieve the saved string from PlayerPrefs
        string retrievedString = RetrieveStringFromPlayerPrefs(stringTestKey);

        // Print the retrieved string to the Unity console with a message
        Debug.Log("Retrieved String: " + retrievedString);
    }

    // Function to save a string value to PlayerPrefs
    private void SaveStringToPlayerPrefs(string key, string value)
    {
        PlayerPrefs.SetString(key, value);
        PlayerPrefs.Save();
    }

    // Function to retrieve a string value from PlayerPrefs
    private string RetrieveStringFromPlayerPrefs(string key)
    {
        return PlayerPrefs.GetString(key, ""); // Provide a default value ("") if the key doesn't exist
    }
}

Second is for int:

using UnityEngine;

public class PlayerPrefsExample : MonoBehaviour
{
    private const string devilsNumberKey = "DevilsNumber";

    void Start()
    {
        // Save the integer value 666 to PlayerPrefs
        SaveIntToPlayerPrefs(devilsNumberKey, 666);

        // Retrieve the saved integer value from PlayerPrefs
        int retrievedValue = RetrieveIntFromPlayerPrefs(devilsNumberKey);

        // Print the retrieved integer value to the Unity console
        Debug.Log("Devil's Number: " + retrievedValue);
    }

    // Function to save an integer value to PlayerPrefs
    private void SaveIntToPlayerPrefs(string key, int value)
    {
        PlayerPrefs.SetInt(key, value);
        PlayerPrefs.Save();
    }

    // Function to retrieve an integer value from PlayerPrefs
    private int RetrieveIntFromPlayerPrefs(string key)
    {
        return PlayerPrefs.GetInt(key, 0); // Provide a default value (0) if the key doesn't exist
    }
}

And third what stopped my progress with floats:

using UnityEngine;

public class PlayerPrefsFloatTest : MonoBehaviour
{
    private const string floatKey = "MyFloatValue";
    private float defaultValue = 0.69f;

    void Start()
    {
        // Save a float value to PlayerPrefs
        SaveFloatToPlayerPrefs(floatKey, defaultValue);

        // Retrieve the saved float value from PlayerPrefs
        float retrievedValue = RetrieveFloatFromPlayerPrefs(floatKey, defaultValue);

        // Log the retrieved float value
        Debug.Log("Retrieved Float Value: " + retrievedValue);
    }

    // Function to save a float value to PlayerPrefs
    private void SaveFloatToPlayerPrefs(string key, float value)
    {
        PlayerPrefs.SetFloat(key, value);
        PlayerPrefs.Save();
    }

    // Function to retrieve a float value from PlayerPrefs
    private float RetrieveFloatFromPlayerPrefs(string key, float defaultValue)
    {
        return PlayerPrefs.GetFloat(key, defaultValue);
    }
}

As you can see there aren’t much changes between them but i’ll post those scripts because why not :face_with_spiral_eyes:

No. It’s just an inherent limitation of the registry. It’s aware of integers and strings which is why you see them but it has no concept of a floating point number. Unity is taking advantage of the fact that a DWORD (a 32-bit unsigned integer) is the same size as a single precision number and storing the float in it using a conversion process.

Just think of it as similar to storing a boolean in an integer as a zero and a one or storing a double as a string.

Technically a floating point number has lower accuracy than an integer. A 32-bit integer has nine digits that can represent the full 0 through 9 while a float (single precision) has approximately 7.

Tested out other regedits and they’re also saying that is a bad value error. :roll_eyes: Quickly tested saving that float to JSON, add that value to playerprefs and convert is back. Result was zero while it supposed to be 0.609. So looks like I need to get back to old drawing board and start thinking of using int and string. It’s going to be a blast when I add in future checkpoint saving method for gameobjects position and rotation. Strings4win. :smile:

Building on this further:

You can convert a float into an int and back and forth losslessly using the BitConverter and Convert classes.

NOTE: when it is an int it is not human-readable in any sense of understanding the floating point value within it.

Here’s some examples from deep inside my Datasacks project:

https://github.com/kurtdekker/datasacks/blob/master/datasacks/Assets/Datasack/Core/DatasackFormatting.cs

1 Like

Alternatively just don’t use PlayerPrefs. It’s not intended for anything beyond preferences which is why it’s lacking support for things like arrays and lists. Most games just store their preferences and save data in My Documents or in AppData which if you’re already working with JSON is trivial to do.

Load:

var path = Path.Combine(Application.persistentDataPath, filename);
var jsonStringData = File.ReadAllText(path);

Save:

var path = Path.Combine(Application.persistentDataPath, filename);
File.WriteAllText(path, jsonStringData);
1 Like

Darn. This would suit my needs just like I dreamed of :slight_smile: Thinking of saving six to eleven gameobjects rotation and position to file through System.Serializable, crypt it to prevent tampering positions+playtime for cheating (my nickname means paranoid in finish :hushed:) and save that file to persistentDataPath. When loading; decrypt, set values for gameobjects and tadah :sunglasses:

PlayerPrefs are good for brightness, volume settings and opened game levels. Thank you for opening new doors to me <3

I don’t quite get why you look up the values in regedit? As Ryiah already said a value like 0.609f would be stored as 0x3f1be76d. Have you actually tried just reading those values back with PlayerPrefs? Especially since you seem to be worried that people could temper with your values. Storing them as floats would actually help a “bit” against tempering. At least against a small number of people who know that Unity stores playerprefs in the registry but don’t know the IEEE754 format. There are online tools like this one which you can use to easily convert a single float value into it’s binary representation and also the hex number of that binary number.

I quickly looked through the registry to find some old value where I might have stored a float and I actually have found one. Unity seems to store 8 bytes instead of 4 bytes as it seems to try to store it as a binary string. It has an int32 value which indicates the actual number of bits (usually 00 00 00 20 ==> 32, so 32 bits) followed by the actual float value. Since regedit expects a DWORD (which means 32 bits) but it actually gets a 64 bit binary value it shows invalid DWORD value which is of course correct. However the value should still be able to be stored and read back by Unity. Or do you try to tamper with it in the registry?

1 Like

After a good night sleep I returned back to the project and checked if there’s another answer to my problem and there it was.

After your text I started to think more thoroughly and had that: “But of course!” -moment.

I was in WYSIWYG -mode :sweat_smile: so if I couldn’t see that float value with my own eyes I thought it didn’t save.
Now I recreated my brightness script to use float instead of that int. Set function to save that value to playerprefs and it’s actually working now flawlessly :smile:

Just need to set DontDestroyOnLoad(gameObject) so settings will follow player to the bitter end :slight_smile:

Thank you for clarification on this subject. I needed gentle slap :stuck_out_tongue: