cz.xprees.variables 1.0.24 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@ ScriptableObject-based variable architecture for decoupled state management, eve
13
13
  `ResetToDefault()`.
14
14
  - **PlayMode Transition Resets** - Declarative `[ResetOnPlayMode]` attribute controls automatic reset when entering or exiting Play Mode.
15
15
  - **State Lifetime Control** - Inherits `DescriptionBaseSO.lifetime` (`Scenario`, `Session`, `Persistent`) for boundary-aware resets.
16
+ - **Persistent Variables** - `PersistentVariableBaseSO<T>` backs a variable with `PlayerPrefs` so its value survives across sessions.
16
17
  - **References** - Inspector-switchable references (`ReferenceBase<T>`) supporting both inlined values and shared variable assets.
17
18
  - **Variable Modifiers & Aggregations** - Compose and transform variable streams dynamically.
18
19
 
@@ -84,3 +85,32 @@ Every variable ScriptableObject exposes the **Lifetime** dropdown in the Inspect
84
85
  [StatefulLifetime(StateLifetime.Persistent)]
85
86
  public class MasterVolumeVariable : FloatVariable { }
86
87
  ```
88
+
89
+ ---
90
+
91
+ ## Persistent Variables (PlayerPrefs)
92
+
93
+ [`PersistentVariableBaseSO<T>`](Runtime/Base/PersistentVariableBaseSO.cs) stores its value in `PlayerPrefs` instead of keeping it purely in memory. It
94
+ is declared `[StatefulLifetime(StateLifetime.Persistent)]` and `[ResetOnPlayMode(PlayModeResetTiming.None)]`, so it is skipped by both the scenario
95
+ snapshot restore and the Play Mode reset, and loads from `PlayerPrefs` on `OnEnable()` instead of cloning `defaultValue`.
96
+
97
+ Ready-made asset types live under `Assets > Create > Variables/Persistent`:
98
+
99
+ | Type | Backing store |
100
+ |----------------------------|-------------------------------|
101
+ | `PersistentBoolVariable` | `PlayerPrefs` int (`0` / `1`) |
102
+ | `PersistentIntVariable` | `PlayerPrefs` int |
103
+ | `PersistentFloatVariable` | `PlayerPrefs` float |
104
+ | `PersistentStringVariable` | `PlayerPrefs` string |
105
+
106
+ Key points:
107
+
108
+ - **`playerPrefsKey` is required.** There is no implicit key - an empty key logs an error and the value is not stored.
109
+ - **Write-through.** Every `CurrentValue` set writes to `PlayerPrefs` before `onValueChanged` fires. Unity flushes to disk on quit and focus loss;
110
+ call `Flush()` when an immediate write is required.
111
+ - **`defaultValue` is the fallback** used when the key is not present yet. `ResetToDefault()` also overwrites the stored value, while
112
+ `ClearStoredValue()` deletes the key.
113
+ - **Other types** can extend [`PersistentJsonVariableSO<T>`](Runtime/Base/PersistentJsonVariableSO.cs), which stores `JsonUtility` JSON in a string
114
+ key. `JsonUtility` only round-trips `[Serializable]` classes and structs - top level primitives, enums and arrays need their own
115
+ `PersistentVariableBaseSO<T>` subclass.
116
+ - The inherited **Lifetime** dropdown is inert on these assets, because the class-level `[StatefulLifetime]` attribute takes precedence.
@@ -0,0 +1,30 @@
1
+ using System;
2
+ using UnityEngine;
3
+
4
+ namespace Xprees.Variables.Base
5
+ {
6
+ /// Base class for persistent variables of arbitrary serializable types, stored as JSON in PlayerPrefs.
7
+ /// Only [Serializable] classes and structs round-trip through JsonUtility - top level primitives,
8
+ /// enums, and arrays are not supported and need a dedicated subclass of PersistentVariableBaseSO.
9
+ /// <typeparam name="T">Unity-Serializable or System.Serializable class or struct</typeparam>
10
+ public abstract class PersistentJsonVariableSO<T> : PersistentVariableBaseSO<T>
11
+ {
12
+ protected override T ReadStoredValue(string key, T fallback)
13
+ {
14
+ var json = PlayerPrefs.GetString(key, string.Empty);
15
+ if (string.IsNullOrEmpty(json) || json == "{}" || json == "null") return fallback;
16
+
17
+ try
18
+ {
19
+ return JsonUtility.FromJson<T>(json);
20
+ }
21
+ catch (Exception exception)
22
+ {
23
+ Debug.LogError($"Failed to deserialize persistent variable '{name}' from PlayerPrefs key '{key}': {exception.Message}", this);
24
+ return fallback;
25
+ }
26
+ }
27
+
28
+ protected override void WriteStoredValue(string key, T value) => PlayerPrefs.SetString(key, JsonUtility.ToJson(value));
29
+ }
30
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: c2c5bcd7dffd19540970331068c40fc3
@@ -0,0 +1,94 @@
1
+ using UnityEngine;
2
+ using Xprees.Core;
3
+ using Xprees.Variables.Utils;
4
+
5
+ namespace Xprees.Variables.Base
6
+ {
7
+ /// Base class for variable ScriptableObjects, whose value is backed by PlayerPrefs and survives across sessions.
8
+ /// The value is written through on every set and loaded when the asset is enabled.
9
+ /// Opted out of both the Play Mode reset and the StateSnapshotService scenario restore.
10
+ /// <typeparam name="T">Unity Serializable or System.Serializable</typeparam>
11
+ [StatefulLifetime(StateLifetime.Persistent)]
12
+ [ResetOnPlayMode(PlayModeResetTiming.None)]
13
+ public abstract class PersistentVariableBaseSO<T> : VariableBaseSO<T>
14
+ {
15
+ [Header("Persistence Settings")]
16
+ [Tooltip("PlayerPrefs key backing this variable. Required and must be unique across the project.")]
17
+ [SerializeField] private string playerPrefsKey;
18
+
19
+ public string PlayerPrefsKey => playerPrefsKey;
20
+
21
+ public bool HasStoredValue => !string.IsNullOrEmpty(playerPrefsKey) && PlayerPrefs.HasKey(playerPrefsKey);
22
+
23
+ public override T CurrentValue
24
+ {
25
+ get => base.CurrentValue;
26
+ set
27
+ {
28
+ // Persist before notifying so listeners always observe an already stored value
29
+ Persist(value);
30
+ base.CurrentValue = value;
31
+ }
32
+ }
33
+
34
+ #if UNITY_EDITOR
35
+ protected override void OnValidate()
36
+ {
37
+ if (string.IsNullOrEmpty(playerPrefsKey))
38
+ {
39
+ Debug.LogError($"Persistent variable '{name}' has no PlayerPrefs key assigned and will not be stored.", this);
40
+ }
41
+
42
+ // Unlike a regular variable, the Editor should show what is actually stored, not the authored default.
43
+ if (!Application.isPlaying) currentValue = ReadStoredOrDefault();
44
+ }
45
+ #endif
46
+
47
+ /// Reads the stored value from PlayerPrefs, returning the fallback when no value is stored.
48
+ protected abstract T ReadStoredValue(string key, T fallback);
49
+
50
+ /// Writes the value to PlayerPrefs under the given key.
51
+ protected abstract void WriteStoredValue(string key, T value);
52
+
53
+ protected override void InitializeValue() => Load();
54
+
55
+ /// Loads the value from PlayerPrefs, falling back to the authored default when nothing is stored, and notifies listeners.
56
+ public void Load()
57
+ {
58
+ currentValue = ReadStoredOrDefault();
59
+ onValueChanged?.Invoke(currentValue);
60
+ }
61
+
62
+ /// Resets the variable to its authored default value and overwrites the stored value with it.
63
+ public override void ResetToDefault() => CurrentValue = CloningTools.Clone(defaultValue);
64
+
65
+ /// Deletes the stored value and resets the variable to its authored default value.
66
+ public void ClearStoredValue()
67
+ {
68
+ if (!string.IsNullOrEmpty(playerPrefsKey)) PlayerPrefs.DeleteKey(playerPrefsKey);
69
+ base.ResetToDefault();
70
+ }
71
+
72
+ /// Forces PlayerPrefs to write to disk immediately.
73
+ /// Rarely needed - Unity flushes on quit and on focus loss.
74
+ public void Flush() => PlayerPrefs.Save();
75
+
76
+ /// Reads the stored value without notifying listeners, falling back to a fresh clone of the authored default value.
77
+ private T ReadStoredOrDefault()
78
+ {
79
+ var fallback = CloningTools.Clone(defaultValue);
80
+ return HasStoredValue ? ReadStoredValue(playerPrefsKey, fallback) : fallback;
81
+ }
82
+
83
+ private void Persist(T value)
84
+ {
85
+ if (string.IsNullOrEmpty(playerPrefsKey))
86
+ {
87
+ Debug.LogError($"Persistent variable '{name}' has no PlayerPrefs key assigned - value was not stored.", this);
88
+ return;
89
+ }
90
+
91
+ WriteStoredValue(playerPrefsKey, value);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: ea26ad77ede55db4ca446b8ba81bd416
@@ -10,9 +10,12 @@ namespace Xprees.Variables.Base
10
10
  protected virtual void OnEnable()
11
11
  {
12
12
  hideFlags = HideFlags.DontUnloadUnusedAsset;
13
- ResetToDefault();
13
+ InitializeValue();
14
14
  }
15
15
 
16
+ /// Initializes the runtime value when the asset is loaded. Defaults to the authored default value.
17
+ protected virtual void InitializeValue() => ResetToDefault();
18
+
16
19
  /// Resets the variable to its authored default value.
17
20
  public abstract void ResetToDefault();
18
21
  }
@@ -28,7 +31,7 @@ namespace Xprees.Variables.Base
28
31
  [SerializeField] protected T defaultValue;
29
32
 
30
33
  [Tooltip("Current value of variable - Runtime only value. Replaced with default value on OnEnable/PlayMode start.")]
31
- [SerializeField] private T currentValue;
34
+ [SerializeField] protected T currentValue;
32
35
 
33
36
  #if UNITY_EDITOR
34
37
  protected virtual void OnValidate()
@@ -0,0 +1,13 @@
1
+ using UnityEngine;
2
+ using Xprees.Variables.Base;
3
+
4
+ namespace Xprees.Variables.Persistent
5
+ {
6
+ [CreateAssetMenu(menuName = "Variables/Persistent/Bool", fileName = "Bool_PersistVar")]
7
+ public class PersistentBoolVariable : PersistentVariableBaseSO<bool>
8
+ {
9
+ protected override bool ReadStoredValue(string key, bool fallback) => PlayerPrefs.GetInt(key, fallback ? 1 : 0) != 0;
10
+
11
+ protected override void WriteStoredValue(string key, bool value) => PlayerPrefs.SetInt(key, value ? 1 : 0);
12
+ }
13
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 80c182662bc041b4aa60856e2d194282
@@ -0,0 +1,13 @@
1
+ using UnityEngine;
2
+ using Xprees.Variables.Base;
3
+
4
+ namespace Xprees.Variables.Persistent
5
+ {
6
+ [CreateAssetMenu(menuName = "Variables/Persistent/Float", fileName = "Float_PersistVar")]
7
+ public class PersistentFloatVariable : PersistentVariableBaseSO<float>
8
+ {
9
+ protected override float ReadStoredValue(string key, float fallback) => PlayerPrefs.GetFloat(key, fallback);
10
+
11
+ protected override void WriteStoredValue(string key, float value) => PlayerPrefs.SetFloat(key, value);
12
+ }
13
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: ff48fa2ba5f54504c86a2c71c5dde1ec
@@ -0,0 +1,13 @@
1
+ using UnityEngine;
2
+ using Xprees.Variables.Base;
3
+
4
+ namespace Xprees.Variables.Persistent
5
+ {
6
+ [CreateAssetMenu(menuName = "Variables/Persistent/Int", fileName = "Int_PersistVar")]
7
+ public class PersistentIntVariable : PersistentVariableBaseSO<int>
8
+ {
9
+ protected override int ReadStoredValue(string key, int fallback) => PlayerPrefs.GetInt(key, fallback);
10
+
11
+ protected override void WriteStoredValue(string key, int value) => PlayerPrefs.SetInt(key, value);
12
+ }
13
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 93a449f7d0ee70e4bb21defcfbb8fc25
@@ -0,0 +1,13 @@
1
+ using UnityEngine;
2
+ using Xprees.Variables.Base;
3
+
4
+ namespace Xprees.Variables.Persistent
5
+ {
6
+ [CreateAssetMenu(menuName = "Variables/Persistent/String", fileName = "String_PersistVar")]
7
+ public class PersistentStringVariable : PersistentVariableBaseSO<string>
8
+ {
9
+ protected override string ReadStoredValue(string key, string fallback) => PlayerPrefs.GetString(key, fallback);
10
+
11
+ protected override void WriteStoredValue(string key, string value) => PlayerPrefs.SetString(key, value);
12
+ }
13
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 04b7f2b4fe108b64a9046f367ed734ce
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 2783884d6a4a28d40b847300ec2d9414
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cz.xprees.variables",
3
3
  "displayName": "Variables",
4
- "version": "1.0.24",
4
+ "version": "1.0.25",
5
5
  "unity": "2021.3",
6
6
  "description": "This package contains a system for creating variables based on ScriptableObjects in Editor.",
7
7
  "license": "Apache-2.0",