Я хочу прочитать данные из реестра в своем автономном приложении WPF, которое создается внутри игры на Unity с помощью PlayerPrefs. Вопрос в том, как читать и записывать float в реестр как DWORD32.
public static float Int64ToFloat(long value)
{
byte[] bytes = BitConverter.GetBytes(value);
byte[] floatBytes = new byte[4];
Array.Copy(bytes, floatBytes, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(floatBytes);
float floatValue = BitConverter.ToSingle(floatBytes, 0);
return floatValue;
}
Этот код возвращает очень маленькие значения, например 1,35E-43.
@MaxPlay Да, я посмотрел значения в реестре, но, честно говоря, мне это не особо помогло, так как я никогда не работал с таким кодированием, поэтому на глаз сложно сказать, что это такое. Если бы у меня был доступ к исходникам класса PlayerPrefs, было бы гораздо проще.





Методы представления целых типов и чисел с плавающей запятой различны.
Возьмем пример
Целочисленные типы можно преобразовать в интуитивно понятный двоичный формат следующим образом:
целое число 11 = 00001011
но.
Представление с плавающей запятой немного отличается.
В качестве примера возьмем 8-битное число.
01011111
Первые 4 цифры этого числа представляют собой целое число
0101 = 5
Последние 4 цифры рассчитываются следующим образом:
1111 = 0,5 + 0,25 + 0,125 + 0,0625
Каждая цифра представляет собой 2 в степени -n.
Если вы хотите отрезать часть длинного типа и использовать его как целое число, а последнее число использовать как десятичное и сохранить его в типе с плавающей запятой, существует также следующий метод.
int n = 17012165
float f = n / 10000 // 1701.2165f
Поплавки представлены совсем иначе, чем целые числа.
Целые числа хранятся в 32 битах, где справа налево каждый бит хранит большее число. (0110) => 03+2²+2¹+0⁰, где 0⁰ = 0 равно 6.
Плавающие числа хранятся с использованием знака, показателя степени и дроби, где первый бит — это знак (0 = положительный, 1 = отрицательный), следующие 8 бит — это показатель степени, и это похоже на int, но в конце это ^ (-127). (по крайней мере, в IEEE752), и дробь начинается с первого значения, считая 0 или 2 ^(-1), следующего ^(-2) и так далее до ^(-23). Наконец все подсчитывается вместе:
EXP^(-127) × (1+FRAC
Ваш ответ можно улучшить, добавив дополнительную вспомогательную информацию. Пожалуйста, отредактируйте , добавив дополнительную информацию, например цитаты или документацию, чтобы другие могли подтвердить правильность вашего ответа. Более подробную информацию о том, как писать хорошие ответы, вы можете найти в справочном центре.
Unity сохраняет не float, а double! Это действительно довольно хитрый хак, но если вы присмотритесь, то обнаружите, что ваш DWORD (32bit) действительно содержит 64-бит long! (понятия не имею, почему они там не использовали должным образом QWORD или прямо двоичный код)
Как уже упоминалось для типов с плавающей запятой, вы не можете просто вырезать bytes и ожидать, что значение останется таким же, как вы могли бы сделать с long -> int.
Вы можете просто использовать
// Have in mind that the key here is NOT the one you use in Unity but also contains a certain trailing hash string
public static float GetFloat(string key, float fallback = 0f)
{
using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(@"Software\Unity\UnityEditor\" + Application.companyName + "\\" + Application.productName))
{
var value = registryKey?.GetValue(key);
if (value is long longValue)
{
// Convert the 64-bit value to a double
var doubleValue = BitConverter.Int64BitsToDouble(longValue);
// then simply cast to cut its precision down
var floatValue = (float)doubleValue;
return floatValue;
}
}
return fallback;
}
Чтобы вы проверили
var input = 12345678901234567890.1234567890f;
PlayerPrefs.SetFloat("TestFloat", input);
var output = GetFloat("TestFloat_h1435782019");
Assert.AreApproximatelyEqual(input,output, "Value retrieved from registry does not match the value stored in PlayerPrefs.");
Я тоже наткнулась на этот фрагмент
Если мы можем этому доверять, хеш-строка в основном рассчитывается как
public class PlayerPrefsHash { // Returns the string property name Unity would generate for a player prefs registry key value // e.g., "PlayerGold" -> "PlayerGold_hXXXXXXXXXXXXXX" public static string Hash(string name) { uint hash = 5381; foreach (char c in name) hash = hash * 33 ^ c; string key = name + "_h" + hash; return key; } }
Таким образом, вы можете использовать это в моем методе выше и иметь возможность использовать ту же ключевую константу в Unity, что и на стороне WPF.
И, по крайней мере, мой быстрый тест показал, что это точно.
var key = PlayerPrefsHash.Hash("TestFloat");
Assert.AreEqual("TestFloat_h1435782019", key, "PlayerPrefsHash.Hash does not match PlayerPrefs generated hash.");
Использовали ли вы regedit для просмотра сохраненного значения? Возможно, Unity не использует стандартную кодировку для значений с плавающей запятой.