{"id":2799,"date":"2022-12-06T12:54:30","date_gmt":"2022-12-06T04:54:30","guid":{"rendered":"http:\/\/www.u3d8.com\/?p=2799"},"modified":"2022-12-06T12:54:32","modified_gmt":"2022-12-06T04:54:32","slug":"unity%e4%b9%8bplayerprefs%e7%bc%96%e8%be%91%e5%99%a8","status":"publish","type":"post","link":"http:\/\/www.u3d8.com\/?p=2799","title":{"rendered":"Unity\u4e4bPlayerPrefs\u7f16\u8f91\u5668"},"content":{"rendered":"\n<p>\u67e5\u770bPlayerPrefs\u5b58\u50a8\u5185\u5bb9\uff0c\u53ca\u7f16\u8f91\u5b58\u50a8<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/www.u3d8.com\/wp-content\/uploads\/2022\/12\/Snipaste_2022-12-06_12-54-03.jpg\" data-lightbox=\"image_lg\"><img title=\"Unity\u4e4bPlayerPrefs\u7f16\u8f91\u5668 - \u7b2c1\u5f20  | u3d8\u6280\u672f\u5206\u4eab\" alt=\"Unity\u4e4bPlayerPrefs\u7f16\u8f91\u5668 - \u7b2c1\u5f20  | u3d8\u6280\u672f\u5206\u4eab\"  loading=\"lazy\" width=\"967\" height=\"566\"  data-src=\"http:\/\/www.u3d8.com\/wp-content\/uploads\/2022\/12\/Snipaste_2022-12-06_12-54-03.jpg\" alt=\"\" class=\"wp-image-2800\" srcset=\"http:\/\/www.u3d8.com\/wp-content\/uploads\/2022\/12\/Snipaste_2022-12-06_12-54-03.jpg 967w, http:\/\/www.u3d8.com\/wp-content\/uploads\/2022\/12\/Snipaste_2022-12-06_12-54-03-300x176.jpg 300w, http:\/\/www.u3d8.com\/wp-content\/uploads\/2022\/12\/Snipaste_2022-12-06_12-54-03-768x450.jpg 768w\" sizes=\"(max-width: 967px) 100vw, 967px\" \/><\/a><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>#if UNITY_EDITOR_WIN\r\n\r\nusing Microsoft.Win32;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing UnityEditor;\r\nusing UnityEditor.IMGUI.Controls;\r\nusing UnityEngine;\r\n\r\npublic class PlayerPrefsEditor : EditorWindow\r\n{\r\n    private enum PrefType { Float = 0, Int, String, Bool };\r\n\r\n    &#91;Serializable]\r\n    private struct PrefPair\r\n    {\r\n        public string Key { get; set; }\r\n        public object Value { get; set; }\r\n    }\r\n\r\n    private static readonly Encoding encoding = new UTF8Encoding();\r\n    private static readonly DateTime missingDateTime = new DateTime(1601, 1, 1);\r\n\r\n    private bool showEditorPrefs;\r\n    private SearchField searchField;\r\n    private List&lt;PrefPair> deserializedPlayerPrefs = new List&lt;PrefPair>();\r\n    private readonly List&lt;PrefPair> filteredPlayerPrefs = new List&lt;PrefPair>();\r\n    private DateTime? lastDeserialization;\r\n    private Vector2 scrollPosition;\r\n    private Vector2 lastScrollPosition;\r\n    private int inspectorUpdateFrame;\r\n    private string searchFilter = string.Empty;\r\n    private string keyQueuedForDeletion;\r\n    private PrefType newEntryType = PrefType.String;\r\n    private string newEntryKey = \"\";\r\n    private float newEntryValueFloat;\r\n    private int newEntryValueInt;\r\n    private bool newEntryValueBool;\r\n    private string newEntryValueString = \"\";\r\n\r\n    private void OnEnable ()\r\n    {\r\n        searchField = new SearchField();\r\n    }\r\n\r\n    &#91;MenuItem(\"Window\/PlayerPrefs Editor\")]\r\n    private static void OpenWindow ()\r\n    {\r\n        var editor = GetWindow&lt;PlayerPrefsEditor>(\"Prefs Editor\", true);\r\n        editor.titleContent = new GUIContent(\"Prefs Editor\", EditorGUIUtility.IconContent(\"Settings\").image);\r\n        editor.minSize = new Vector2(230, 400);\r\n    }\r\n\r\n    private void OnGUI ()\r\n    {\r\n        EditorGUILayout.Space();\r\n\r\n        DrawTopBar();\r\n\r\n        if (!lastDeserialization.HasValue || DateTime.UtcNow - lastDeserialization.Value > TimeSpan.FromMilliseconds(500))\r\n        {\r\n            deserializedPlayerPrefs = new List&lt;PrefPair>(RetrieveSavedPrefs(PlayerSettings.companyName, PlayerSettings.productName));\r\n            lastDeserialization = DateTime.UtcNow;\r\n        }\r\n\r\n        DrawMainList();\r\n        DrawAddEntry();\r\n        DrawBottomMenu();\r\n\r\n        EditorGUILayout.Space();\r\n\r\n        \/\/ If the user has scrolled, deselect - this is because control IDs within carousel will change when scrolled\r\n        \/\/ so we'd end up with the wrong box selected.\r\n        if (scrollPosition != lastScrollPosition) GUI.FocusControl(\"\");\r\n    }\r\n\r\n    private void OnInspectorUpdate ()\r\n    {\r\n        \/\/ If a PlayerPref has been specified for deletion.\r\n        if (!string.IsNullOrEmpty(keyQueuedForDeletion))\r\n        {\r\n            \/\/ If the user just deleted a PlayerPref, find the ID and defer it for deletion by OnInspectorUpdate().\r\n            if (deserializedPlayerPrefs != null)\r\n            {\r\n                var entryCount = deserializedPlayerPrefs.Count;\r\n                for (int i = 0; i &lt; entryCount; i++)\r\n                {\r\n                    if (deserializedPlayerPrefs&#91;i].Key == keyQueuedForDeletion)\r\n                    {\r\n                        deserializedPlayerPrefs.RemoveAt(i);\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n\r\n            \/\/ Remove the queued key since we've just deleted it.\r\n            keyQueuedForDeletion = null;\r\n\r\n            \/\/ Update the search results and repaint the window.\r\n            UpdateSearch();\r\n            Repaint();\r\n        }\r\n        else if (inspectorUpdateFrame % 10 == 0) \/\/ Once a second (every 10th frame)\r\n        {\r\n            \/\/ Force the window to repaint.\r\n            Repaint();\r\n        }\r\n\r\n        \/\/ Track what frame we're on, so we can call code less often.\r\n        inspectorUpdateFrame++;\r\n    }\r\n\r\n    private void DeleteAll ()\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.DeleteAll();\r\n        else PlayerPrefs.DeleteAll();\r\n    }\r\n\r\n    private void DeleteKey (string key)\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.DeleteKey(key);\r\n        else PlayerPrefs.DeleteKey(key);\r\n    }\r\n\r\n    private int GetInt (string key, int defaultValue = 0)\r\n    {\r\n        if (showEditorPrefs) return EditorPrefs.GetInt(key, defaultValue);\r\n        return PlayerPrefs.GetInt(key, defaultValue);\r\n    }\r\n\r\n    private float GetFloat (string key, float defaultValue = 0.0f)\r\n    {\r\n        if (showEditorPrefs) return EditorPrefs.GetFloat(key, defaultValue);\r\n        return PlayerPrefs.GetFloat(key, defaultValue);\r\n    }\r\n\r\n    private string GetString (string key, string defaultValue = \"\")\r\n    {\r\n        if (showEditorPrefs) return EditorPrefs.GetString(key, defaultValue);\r\n        return PlayerPrefs.GetString(key, defaultValue);\r\n    }\r\n\r\n    private bool GetBool (string key, bool defaultValue = false)\r\n    {\r\n        if (showEditorPrefs) return EditorPrefs.GetBool(key, defaultValue);\r\n        throw new NotSupportedException(\"PlayerPrefs interface does not natively support booleans.\");\r\n    }\r\n\r\n    private void SetInt (string key, int value)\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.SetInt(key, value);\r\n        else PlayerPrefs.SetInt(key, value);\r\n    }\r\n\r\n    private void SetFloat (string key, float value)\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.SetFloat(key, value);\r\n        else PlayerPrefs.SetFloat(key, value);\r\n    }\r\n\r\n    private void SetString (string key, string value)\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.SetString(key, value);\r\n        else PlayerPrefs.SetString(key, value);\r\n    }\r\n\r\n    private void SetBool (string key, bool value)\r\n    {\r\n        if (showEditorPrefs) EditorPrefs.SetBool(key, value);\r\n        else throw new NotSupportedException(\"PlayerPrefs interface does not natively support booleans.\");\r\n    }\r\n\r\n    private void Save ()\r\n    {\r\n        if (showEditorPrefs) { } \r\n        else PlayerPrefs.Save();\r\n    }\r\n\r\n    \/\/\/ &lt;summary>\r\n    \/\/\/ This returns an array of the stored PlayerPrefs from the Windows registry, to allow \r\n    \/\/\/ us to to look up what's actually in the PlayerPrefs. This is used as a kind of lookup table.\r\n    \/\/\/ &lt;\/summary>\r\n    private PrefPair&#91;] RetrieveSavedPrefs (string companyName, string productName)\r\n    {\r\n        RegistryKey registryKey;\r\n\r\n        if (showEditorPrefs)\r\n        {\r\n            var majorVersion = Application.unityVersion.Split('.')&#91;0];\r\n            registryKey = Registry.CurrentUser.OpenSubKey(\"Software\\\\Unity Technologies\\\\Unity Editor \" + majorVersion + \".x\");\r\n        }\r\n        \/\/ On Windows, PlayerPrefs are stored in the registry under HKCU\\Software\\&#91;company name]\\&#91;product name] key, where company and product names are the names set up in Project Settings.\r\n        else registryKey = Registry.CurrentUser.OpenSubKey(\"Software\\\\Unity\\\\UnityEditor\\\\\" + companyName + \"\\\\\" + productName);\r\n\r\n        \/\/ No prefs saved for the project.\r\n        if (registryKey is null) return Array.Empty&lt;PrefPair>();\r\n\r\n        var valueNames = registryKey.GetValueNames();\r\n        var tempPlayerPrefs = new PrefPair&#91;valueNames.Length];\r\n\r\n        for (int i = 0; i &lt; valueNames.Length; i++)\r\n        {\r\n            var valueName = valueNames&#91;i];\r\n            var key = valueNames&#91;i];\r\n\r\n            \/\/ Remove the _h193410979 style suffix used on PlayerPref keys in Windows registry.\r\n            var index = key.LastIndexOf(\"_\", StringComparison.Ordinal);\r\n            key = key.Remove(index, key.Length - index);\r\n\r\n            var ambiguousValue = registryKey.GetValue(valueName);\r\n\r\n            \/\/ Unfortunately floats will come back as an int (at least on 64 bit) because the float is stored as\r\n            \/\/ 64 bit but marked as 32 bit - which confuses the GetValue() method greatly! \r\n            if (ambiguousValue is int)\r\n            {\r\n                \/\/ If the PlayerPref is not actually an int then it must be a float, this will evaluate to true\r\n                \/\/ (impossible for it to be 0 and -1 at the same time).\r\n                if (GetInt(key, -1) == -1 &amp;&amp; GetInt(key) == 0)\r\n                    ambiguousValue = GetFloat(key);\r\n                \/\/ If it reports a non default value as a bool, it's a bool not a string.\r\n                else if (showEditorPrefs &amp;&amp; (GetBool(key, true) != true || GetBool(key)))\r\n                    ambiguousValue = GetBool(key);\r\n            }\r\n            else if (ambiguousValue.GetType() == typeof(byte&#91;]))\r\n                \/\/ On Unity 5 a string may be stored as binary, so convert it back to a string.\r\n                ambiguousValue = encoding.GetString((byte&#91;])ambiguousValue).TrimEnd('\\0');\r\n\r\n            tempPlayerPrefs&#91;i] = new PrefPair() { Key = key, Value = ambiguousValue };\r\n        }\r\n\r\n        return tempPlayerPrefs;\r\n    }\r\n\r\n    private void UpdateSearch ()\r\n    {\r\n        filteredPlayerPrefs.Clear();\r\n\r\n        if (string.IsNullOrEmpty(searchFilter)) return;\r\n\r\n        var entryCount = deserializedPlayerPrefs.Count;\r\n\r\n        for (int i = 0; i &lt; entryCount; i++)\r\n        {\r\n            var fullKey = deserializedPlayerPrefs&#91;i].Key;\r\n            var displayKey = fullKey;\r\n\r\n            if (displayKey.ToLower().Contains(searchFilter.ToLower()))\r\n                filteredPlayerPrefs.Add(deserializedPlayerPrefs&#91;i]);\r\n        }\r\n    }\r\n\r\n    private void DrawTopBar ()\r\n    {\r\n        var newSearchFilter = searchField.OnGUI(searchFilter);\r\n        GUILayout.Space(4);\r\n\r\n        \/\/ If the requested search filter has changed.\r\n        if (newSearchFilter != searchFilter)\r\n        {\r\n            searchFilter = newSearchFilter;\r\n            \/\/ Trigger UpdateSearch to calculate new search results.\r\n            UpdateSearch();\r\n        }\r\n\r\n        \/\/ Allow the user to toggle between editor and PlayerPrefs.\r\n        var oldIndex = showEditorPrefs ? 1 : 0;\r\n        var newIndex = GUILayout.Toolbar(oldIndex, new&#91;] { \"PlayerPrefs\", \"EditorPrefs\" });\r\n\r\n        \/\/ Has the toggle changed?\r\n        if (newIndex != oldIndex)\r\n        {\r\n            \/\/ Reset.\r\n            lastDeserialization = null;\r\n            showEditorPrefs = newIndex == 1;\r\n        }\r\n    }\r\n\r\n    private void DrawMainList ()\r\n    {\r\n        \/\/ The bold table headings.\r\n        EditorGUILayout.BeginHorizontal();\r\n        GUILayout.Label(\"Key\", EditorStyles.boldLabel);\r\n        GUILayout.Label(\"Value\", EditorStyles.boldLabel);\r\n        GUILayout.Label(\"Type\", EditorStyles.boldLabel, GUILayout.Width(37));\r\n        GUILayout.Label(\"Del\", EditorStyles.boldLabel, GUILayout.Width(25));\r\n        EditorGUILayout.EndHorizontal();\r\n\r\n        var textFieldStyle = new GUIStyle(GUI.skin.textField);\r\n        var activePlayerPrefs = deserializedPlayerPrefs;\r\n\r\n        if (!string.IsNullOrEmpty(searchFilter))\r\n            activePlayerPrefs = filteredPlayerPrefs;\r\n\r\n        int entryCount = activePlayerPrefs.Count;\r\n        lastScrollPosition = scrollPosition;\r\n\r\n        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);\r\n        if (scrollPosition.y &lt; 0) scrollPosition.y = 0;\r\n\r\n        \/\/ The following code has been optimised so that rather than attempting to draw UI for every single PlayerPref\r\n        \/\/ it instead only draws the UI for those currently visible in the scroll view and pads above and below those\r\n        \/\/ results to maintain the right size using GUILayout.Space(). This enables us to work with thousands of \r\n        \/\/ PlayerPrefs without slowing the interface to a halt.\r\n\r\n        var rowHeight = 18;\r\n        var visibleCount = Mathf.CeilToInt((float)Screen.height \/ rowHeight);\r\n        var firstShownIndex = Mathf.FloorToInt(scrollPosition.y \/ rowHeight);\r\n        var shownIndexLimit = firstShownIndex + visibleCount;\r\n\r\n        if (entryCount &lt; shownIndexLimit) shownIndexLimit = entryCount;\r\n\r\n        \/\/ If the number of displayed PlayerPrefs is smaller than the number we can display (like we're at the end\r\n        \/\/ of the list) then move the starting index back to adjust.\r\n        if (shownIndexLimit - firstShownIndex &lt; visibleCount)\r\n            firstShownIndex -= visibleCount - (shownIndexLimit - firstShownIndex);\r\n\r\n        \/\/ Can't have a negative index of a first shown PlayerPref, so clamp to 0\r\n        if (firstShownIndex &lt; 0) firstShownIndex = 0;\r\n\r\n        \/\/ Pad above the on screen results so that we're not wasting draw calls on invisible UI and the drawn player\r\n        \/\/ prefs end up in the same place in the list.\r\n        GUILayout.Space(firstShownIndex * rowHeight);\r\n\r\n        for (int i = firstShownIndex; i &lt; shownIndexLimit; i++)\r\n        {\r\n            textFieldStyle.normal.textColor = GUI.skin.textField.normal.textColor;\r\n            textFieldStyle.focused.textColor = GUI.skin.textField.focused.textColor;\r\n\r\n            var fullKey = activePlayerPrefs&#91;i].Key;\r\n            var displayKey = fullKey;\r\n            var deserializedValue = activePlayerPrefs&#91;i].Value;\r\n\r\n            EditorGUILayout.BeginHorizontal();\r\n\r\n            var valueType = deserializedValue.GetType();\r\n            EditorGUILayout.TextField(displayKey, textFieldStyle);\r\n\r\n            if (valueType == typeof(float))\r\n            {\r\n                var initialValue = GetFloat(fullKey);\r\n                var newValue = EditorGUILayout.FloatField(initialValue, textFieldStyle);\r\n                if (!Mathf.Approximately(newValue, initialValue))\r\n                {\r\n                    SetFloat(fullKey, newValue);\r\n                    Save();\r\n                }\r\n                GUILayout.Label(\"float\", GUILayout.Width(37));\r\n            }\r\n            else if (valueType == typeof(int))\r\n            {\r\n                var initialValue = GetInt(fullKey);\r\n                int newValue = EditorGUILayout.IntField(initialValue, textFieldStyle);\r\n                if (newValue != initialValue)\r\n                {\r\n                    SetInt(fullKey, newValue);\r\n                    Save();\r\n                }\r\n                GUILayout.Label(\"int\", GUILayout.Width(37));\r\n            }\r\n            else if (valueType == typeof(bool))\r\n            {\r\n                var initialValue = GetBool(fullKey);\r\n                var newValue = EditorGUILayout.Toggle(initialValue);\r\n                if (newValue != initialValue)\r\n                {\r\n                    SetBool(fullKey, newValue);\r\n                    Save();\r\n                }\r\n                GUILayout.Label(\"bool\", GUILayout.Width(37));\r\n            }\r\n            else if (valueType == typeof(string))\r\n            {\r\n                var initialValue = GetString(fullKey);\r\n                var newValue = EditorGUILayout.TextField(initialValue, textFieldStyle);\r\n                if (newValue != initialValue)\r\n                {\r\n                    SetString(fullKey, newValue);\r\n                    Save();\r\n                }\r\n                GUILayout.Label(\"string\", GUILayout.Width(37));\r\n            }\r\n\r\n            if (GUILayout.Button(\"X\", GUILayout.Width(25)))\r\n            {\r\n                DeleteKey(fullKey);\r\n                Save();\r\n                DeleteCachedRecord(fullKey);\r\n            }\r\n            EditorGUILayout.EndHorizontal();\r\n        }\r\n\r\n        var bottomPadding = (entryCount - shownIndexLimit) * rowHeight;\r\n        if (bottomPadding > 0) GUILayout.Space(bottomPadding);\r\n\r\n        EditorGUILayout.EndScrollView();\r\n\r\n        GUILayout.Label(\"Entry Count: \" + entryCount);\r\n\r\n        var rect = GUILayoutUtility.GetLastRect();\r\n        rect.height = 1;\r\n        rect.y -= 4;\r\n        EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));\r\n    }\r\n\r\n    private void DrawAddEntry ()\r\n    {\r\n        var textFieldStyle = new GUIStyle(GUI.skin.textField);\r\n\r\n        EditorGUILayout.Space();\r\n\r\n        GUILayout.Label(showEditorPrefs ? \"Add EditorPref\" : \"Add PlayerPref\", EditorStyles.boldLabel);\r\n\r\n        EditorGUILayout.BeginHorizontal();\r\n\r\n        if (showEditorPrefs)\r\n            newEntryType = (PrefType)GUILayout.Toolbar((int)newEntryType, new&#91;] { \"float\", \"int\", \"string\", \"bool\" });\r\n        else\r\n        {\r\n            if (newEntryType == PrefType.Bool) newEntryType = PrefType.String;\r\n            newEntryType = (PrefType)GUILayout.Toolbar((int)newEntryType, new&#91;] { \"float\", \"int\", \"string\" });\r\n        }\r\n\r\n        EditorGUILayout.EndHorizontal();\r\n\r\n        EditorGUILayout.BeginHorizontal();\r\n        GUILayout.Label(\"Key\", EditorStyles.boldLabel);\r\n        GUILayout.Label(\"Value\", EditorStyles.boldLabel);\r\n        EditorGUILayout.EndHorizontal();\r\n\r\n        EditorGUILayout.BeginHorizontal();\r\n\r\n        GUI.SetNextControlName(\"newEntryKey\");\r\n        newEntryKey = EditorGUILayout.TextField(newEntryKey, textFieldStyle);\r\n        GUI.SetNextControlName(\"newEntryValue\");\r\n\r\n        switch (newEntryType)\r\n        {\r\n            case PrefType.Float: newEntryValueFloat = EditorGUILayout.FloatField(newEntryValueFloat, textFieldStyle); break;\r\n            case PrefType.Int: newEntryValueInt = EditorGUILayout.IntField(newEntryValueInt, textFieldStyle); break;\r\n            case PrefType.String: newEntryValueString = EditorGUILayout.TextField(newEntryValueString, textFieldStyle); break;\r\n            case PrefType.Bool: newEntryValueBool = EditorGUILayout.Toggle(newEntryValueBool); break;\r\n        }\r\n\r\n        \/\/ If the user hit enter while either the key or value fields were being edited.\r\n        var keyboardAddPressed = Event.current.isKey &amp;&amp; Event.current.keyCode == KeyCode.Return &amp;&amp; Event.current.type == EventType.KeyUp &amp;&amp; (GUI.GetNameOfFocusedControl() == \"newEntryKey\" || GUI.GetNameOfFocusedControl() == \"newEntryValue\");\r\n\r\n        \/\/ If the user clicks the Add button or hits return (and there is a non-empty key), create the PlayerPref.\r\n        if ((GUILayout.Button(\"Add\", GUILayout.Width(40)) || keyboardAddPressed) &amp;&amp; !string.IsNullOrEmpty(newEntryKey))\r\n        {\r\n            if (newEntryType == PrefType.Float)\r\n            {\r\n                SetFloat(newEntryKey, newEntryValueFloat);\r\n                CacheRecord(newEntryKey, newEntryValueFloat);\r\n            }\r\n            else if (newEntryType == PrefType.Int)\r\n            {\r\n                SetInt(newEntryKey, newEntryValueInt);\r\n                CacheRecord(newEntryKey, newEntryValueInt);\r\n            }\r\n            else if (newEntryType == PrefType.Bool)\r\n            {\r\n                SetBool(newEntryKey, newEntryValueBool);\r\n                CacheRecord(newEntryKey, newEntryValueBool);\r\n            }\r\n            else\r\n            {\r\n                SetString(newEntryKey, newEntryValueString);\r\n                CacheRecord(newEntryKey, newEntryValueString);\r\n            }\r\n\r\n            Save();\r\n            Repaint();\r\n\r\n            newEntryKey = \"\";\r\n            newEntryValueFloat = 0;\r\n            newEntryValueInt = 0;\r\n            newEntryValueString = \"\";\r\n\r\n            \/\/ Deselect.\r\n            GUI.FocusControl(\"\");\r\n        }\r\n\r\n        EditorGUILayout.EndHorizontal();\r\n    }\r\n\r\n    private void DrawBottomMenu ()\r\n    {\r\n        EditorGUILayout.Space();\r\n\r\n        EditorGUILayout.BeginHorizontal();\r\n        float buttonWidth = (EditorGUIUtility.currentViewWidth - 10) \/ 2f;\r\n        if (GUILayout.Button(\"Delete All Preferences\", GUILayout.Width(buttonWidth)))\r\n        {\r\n            if (EditorUtility.DisplayDialog(\"Delete All?\", \"Are you sure you want to delete all preferences?\", \"Delete All\", \"Cancel\"))\r\n            {\r\n                DeleteAll();\r\n                Save();\r\n                deserializedPlayerPrefs.Clear();\r\n            }\r\n        }\r\n\r\n        GUILayout.FlexibleSpace();\r\n\r\n        if (GUILayout.Button(\"Force Save\", GUILayout.Width(buttonWidth))) Save();\r\n        EditorGUILayout.EndHorizontal();\r\n    }\r\n\r\n    private void CacheRecord (string key, object value)\r\n    {\r\n        \/\/ First of all check if this key already exists, if so replace it's value with the new value\/\r\n        var replaced = false;\r\n        var entryCount = deserializedPlayerPrefs.Count;\r\n        for (int i = 0; i &lt; entryCount; i++)\r\n        {\r\n            if (deserializedPlayerPrefs&#91;i].Key == key)\r\n            {\r\n                deserializedPlayerPrefs&#91;i] = new PrefPair { Key = key, Value = value };\r\n                replaced = true;\r\n                break;\r\n            }\r\n        }\r\n\r\n        \/\/ PlayerPref doesn't already exist (and wasn't replaced) so add it as new.\r\n        if (!replaced)\r\n        {\r\n            \/\/ Cache a PlayerPref the user just created so it can be instantly display (mainly for OSX)\r\n            deserializedPlayerPrefs.Add(new PrefPair { Key = key, Value = value });\r\n        }\r\n\r\n        \/\/ Update the search if it's active\r\n        UpdateSearch();\r\n    }\r\n\r\n    private void DeleteCachedRecord (string fullKey)\r\n    {\r\n        keyQueuedForDeletion = fullKey;\r\n    }\r\n}\r\n\r\n#endif\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u67e5\u770bPlayerPrefs\u5b58\u50a8\u5185\u5bb9\uff0c\u53ca\u7f16\u8f91\u5b58\u50a8<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[297],"tags":[],"_links":{"self":[{"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/posts\/2799"}],"collection":[{"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2799"}],"version-history":[{"count":1,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/posts\/2799\/revisions"}],"predecessor-version":[{"id":2801,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=\/wp\/v2\/posts\/2799\/revisions\/2801"}],"wp:attachment":[{"href":"http:\/\/www.u3d8.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2799"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2799"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.u3d8.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2799"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}