279 lines
9.8 KiB
C#
279 lines
9.8 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
#if UNITY_EDITOR
|
||
using UnityEditor;
|
||
#endif
|
||
|
||
namespace BulletHellTemplate
|
||
{
|
||
/// <summary>
|
||
/// Runtime helper that lets developers inspect and modify a single PlayerPrefs key
|
||
/// while the game is running. Attach it to any GameObject in the scene.
|
||
/// </summary>
|
||
public class PlayerPrefsDebugger : MonoBehaviour
|
||
{
|
||
[Tooltip("PlayerPrefs key to inspect or edit.")]
|
||
public string key = string.Empty;
|
||
|
||
[Tooltip("The value type stored under this key.")]
|
||
public PrefValueType valueType = PrefValueType.Int;
|
||
|
||
[Tooltip("New value to assign when pressing <b>Set Value</b>.")]
|
||
public string newValue = string.Empty;
|
||
|
||
/// <summary>
|
||
/// Returns the value currently stored under <see cref="key"/> in PlayerPrefs.
|
||
/// </summary>
|
||
public string CurrentValue
|
||
{
|
||
get
|
||
{
|
||
switch (valueType)
|
||
{
|
||
case PrefValueType.Int:
|
||
return PlayerPrefs.GetInt(key, 0).ToString();
|
||
case PrefValueType.Float:
|
||
return PlayerPrefs.GetFloat(key, 0f).ToString();
|
||
default:
|
||
return PlayerPrefs.GetString(key, string.Empty);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// The three basic value types supported by Unity's PlayerPrefs API.
|
||
/// </summary>
|
||
public enum PrefValueType { Int, Float, String }
|
||
|
||
/// <summary>
|
||
/// Writes <see cref="newValue"/> to PlayerPrefs using <see cref="key"/> and
|
||
/// the currently selected <see cref="valueType"/>. The method includes basic
|
||
/// parsing validation for numeric values.
|
||
/// </summary>
|
||
public void SetValue()
|
||
{
|
||
if (string.IsNullOrEmpty(key))
|
||
{
|
||
Debug.LogWarning("Key is empty. Nothing was written.");
|
||
return;
|
||
}
|
||
|
||
switch (valueType)
|
||
{
|
||
case PrefValueType.Int:
|
||
if (int.TryParse(newValue, out int i))
|
||
PlayerPrefs.SetInt(key, i);
|
||
else
|
||
Debug.LogError($"'{newValue}' is not a valid integer.");
|
||
break;
|
||
case PrefValueType.Float:
|
||
if (float.TryParse(newValue, out float f))
|
||
PlayerPrefs.SetFloat(key, f);
|
||
else
|
||
Debug.LogError($"'{newValue}' is not a valid float.");
|
||
break;
|
||
default:
|
||
PlayerPrefs.SetString(key, newValue);
|
||
break;
|
||
}
|
||
|
||
PlayerPrefs.Save();
|
||
}
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>
|
||
/// Polished inspector for <see cref="PlayerPrefsDebugger"/>. It is defined inside
|
||
/// a UNITY_EDITOR block so that it is only compiled for the Editor, never for builds.
|
||
/// </summary>
|
||
[CustomEditor(typeof(PlayerPrefsDebugger))]
|
||
public class PlayerPrefsDebuggerInspector : Editor
|
||
{
|
||
SerializedProperty keyProp;
|
||
SerializedProperty typeProp;
|
||
SerializedProperty valueProp;
|
||
|
||
void OnEnable()
|
||
{
|
||
keyProp = serializedObject.FindProperty("key");
|
||
typeProp = serializedObject.FindProperty("valueType");
|
||
valueProp = serializedObject.FindProperty("newValue");
|
||
}
|
||
|
||
public override void OnInspectorGUI()
|
||
{
|
||
serializedObject.Update();
|
||
|
||
EditorGUILayout.PropertyField(keyProp, new GUIContent("Key"));
|
||
EditorGUILayout.PropertyField(typeProp, new GUIContent("Value Type"));
|
||
EditorGUILayout.PropertyField(valueProp, new GUIContent("New Value"));
|
||
|
||
GUILayout.Space(4);
|
||
|
||
if (GUILayout.Button("Get Current Value"))
|
||
{
|
||
var dbg = (PlayerPrefsDebugger)target;
|
||
string current = dbg.CurrentValue;
|
||
EditorUtility.DisplayDialog("Current Value", $"Key: {dbg.key}\nValue: {current}", "OK");
|
||
}
|
||
|
||
if (GUILayout.Button("Set Value"))
|
||
{
|
||
((PlayerPrefsDebugger)target).SetValue();
|
||
}
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Stand‑alone window that offers a quick way to read, write and delete any
|
||
/// PlayerPrefs key. Open it via <c>Tools ▸ BulletHell Template ▸ PlayerPrefs Editor</c>.
|
||
/// </summary>
|
||
public class PlayerPrefsEditorWindow : EditorWindow
|
||
{
|
||
private string key = string.Empty;
|
||
private PlayerPrefsDebugger.PrefValueType valueType = PlayerPrefsDebugger.PrefValueType.Int;
|
||
private string value = string.Empty;
|
||
private Vector2 scroll;
|
||
|
||
[MenuItem("Tools/BulletHell Template/PlayerPrefs Editor", priority = 1)]
|
||
public static void Open()
|
||
{
|
||
GetWindow<PlayerPrefsEditorWindow>("PlayerPrefs Editor");
|
||
}
|
||
|
||
void OnGUI()
|
||
{
|
||
GUILayout.Label("Key Editor", EditorStyles.boldLabel);
|
||
|
||
key = EditorGUILayout.TextField("Key", key);
|
||
valueType = (PlayerPrefsDebugger.PrefValueType)EditorGUILayout.EnumPopup("Value Type", valueType);
|
||
value = EditorGUILayout.TextField("Value", value);
|
||
|
||
GUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("Get"))
|
||
{
|
||
value = GetCurrentValue();
|
||
}
|
||
if (GUILayout.Button("Set"))
|
||
{
|
||
SetCurrentValue();
|
||
}
|
||
if (GUILayout.Button("Delete"))
|
||
{
|
||
if (EditorUtility.DisplayDialog("Delete Key?", $"Remove '{key}' from PlayerPrefs?", "Yes", "No"))
|
||
{
|
||
PlayerPrefs.DeleteKey(key);
|
||
PlayerPrefs.Save();
|
||
value = string.Empty;
|
||
}
|
||
}
|
||
GUILayout.EndHorizontal();
|
||
|
||
GUILayout.Space(10);
|
||
GUILayout.Label("Known Keys (read‑only)", EditorStyles.boldLabel);
|
||
|
||
scroll = GUILayout.BeginScrollView(scroll);
|
||
foreach (var kv in GetPrefixedKeys())
|
||
{
|
||
GUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||
GUILayout.Label(kv.Key, GUILayout.Width(300));
|
||
GUILayout.Label(kv.Value);
|
||
GUILayout.EndHorizontal();
|
||
}
|
||
GUILayout.EndScrollView();
|
||
}
|
||
|
||
string GetCurrentValue()
|
||
{
|
||
switch (valueType)
|
||
{
|
||
case PlayerPrefsDebugger.PrefValueType.Int:
|
||
return PlayerPrefs.GetInt(key, 0).ToString();
|
||
case PlayerPrefsDebugger.PrefValueType.Float:
|
||
return PlayerPrefs.GetFloat(key, 0f).ToString();
|
||
default:
|
||
return PlayerPrefs.GetString(key, string.Empty);
|
||
}
|
||
}
|
||
|
||
void SetCurrentValue()
|
||
{
|
||
switch (valueType)
|
||
{
|
||
case PlayerPrefsDebugger.PrefValueType.Int:
|
||
if (int.TryParse(value, out int i))
|
||
PlayerPrefs.SetInt(key, i);
|
||
else
|
||
ShowParseError("int");
|
||
break;
|
||
case PlayerPrefsDebugger.PrefValueType.Float:
|
||
if (float.TryParse(value, out float f))
|
||
PlayerPrefs.SetFloat(key, f);
|
||
else
|
||
ShowParseError("float");
|
||
break;
|
||
default:
|
||
PlayerPrefs.SetString(key, value);
|
||
break;
|
||
}
|
||
PlayerPrefs.Save();
|
||
}
|
||
|
||
void ShowParseError(string type)
|
||
{
|
||
EditorUtility.DisplayDialog("Parsing Error", $"Value is not a valid {type}.", "OK");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Because Unity does not expose PlayerPrefs enumeration at runtime, we rely on
|
||
/// known prefixes defined in <see cref="PlayerSave"/> to probe for existing keys.
|
||
/// </summary>
|
||
IDictionary<string, string> GetPrefixedKeys()
|
||
{
|
||
var dict = new Dictionary<string, string>();
|
||
string[] prefixes =
|
||
{
|
||
"PLAYERNAME_", "PLAYERICON_", "PLAYERFRAME_", "PLAYERACCOUNTLEVEL_",
|
||
"PLAYERACCOUNTCURRENTEXP_", "TUTORIALISDONE_", "PLAYERSELECTEDCHARACTER_",
|
||
"PLAYERFAVOURITECHARACTER_", "CHARACTERMASTERYLEVEL_", "CHARACTERMASTERYCURRENTEXP_",
|
||
"CHARACTERLEVEL_", "CHARACTERCURRENTEXP_", "ITEMSLOT_", "ITEMLEVEL_",
|
||
"CHARACTERUPGRADELEVEL_", "CHARACTERSKIN_", "CHARACTERUNLOCKEDSKINS_",
|
||
"UNLOCKED_MAPS", "QUEST_PROGRESS_", "QUEST_COMPLETED_", "USED_COUPONS_",
|
||
"LOCAL_DAILYREWARDS_DATA", "LOCAL_NEWPLAYERREWARDS_DATA", "NEXTDAILYRESETTIME"
|
||
};
|
||
|
||
foreach (string prefix in prefixes)
|
||
{
|
||
if (PlayerPrefs.HasKey(prefix))
|
||
dict[prefix] = SafeRead(prefix);
|
||
|
||
if (prefix.EndsWith("_"))
|
||
{
|
||
for (int i = 0; i < 100; i++)
|
||
{
|
||
string keyVariant = prefix + i;
|
||
if (PlayerPrefs.HasKey(keyVariant))
|
||
dict[keyVariant] = SafeRead(keyVariant);
|
||
}
|
||
}
|
||
}
|
||
return dict;
|
||
}
|
||
|
||
string SafeRead(string k)
|
||
{
|
||
// Try int
|
||
int intVal = PlayerPrefs.GetInt(k, int.MinValue);
|
||
if (intVal != int.MinValue) return intVal.ToString();
|
||
// Try float
|
||
float floatVal = PlayerPrefs.GetFloat(k, float.NaN);
|
||
if (!float.IsNaN(floatVal)) return floatVal.ToString();
|
||
// Fallback string
|
||
return PlayerPrefs.GetString(k, string.Empty);
|
||
}
|
||
}
|
||
#endif
|
||
}
|