cz.xprees.variables 1.0.20 → 1.0.23
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 +63 -18
- package/Runtime/Base/ReferenceBase.cs +29 -16
- package/Runtime/Base/VariableAgregationBaseSO.cs +6 -13
- package/Runtime/Base/VariableBaseSO.cs +32 -23
- package/Runtime/Base/VariableModifierBaseSO.cs +7 -11
- package/Runtime/Utils/CloningTools.cs +11 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,29 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/cz.xprees.variables)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
which make it great for storing and simply sharing state without tight coupling.
|
|
7
|
-
Moreover, it provides a way to share variables (state) across multiple scenes making it powerful for multi-scene Unity project.
|
|
5
|
+
ScriptableObject-based variable architecture for decoupled state management, event observation, and multi-scene data sharing in Unity.
|
|
8
6
|
|
|
9
7
|
## Features
|
|
10
8
|
|
|
11
|
-
- **ScriptableObject Variables** -
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- **
|
|
19
|
-
|
|
20
|
-
- **Variable Aggregations** - A set of classes that allow you to aggregate multiple variables into one.
|
|
21
|
-
- For example, you can use the [`BoolAggregation`](Runtime/Aggregations/BoolAggregation.cs) to aggregate multiple boolean variables into one.
|
|
9
|
+
- **ScriptableObject Variables** - Store and share state without coupling systems together.
|
|
10
|
+
- Extend [`VariableBaseSO<T>`](Runtime/Base/VariableBaseSO.cs) to author custom typed variables.
|
|
11
|
+
- Observe runtime changes via the `onValueChanged` event.
|
|
12
|
+
- **Standalone Default State** - Every variable manages its own authored `defaultValue` and deep-clones it into `currentValue` on `OnEnable()` and
|
|
13
|
+
`ResetToDefault()`.
|
|
14
|
+
- **PlayMode Transition Resets** - Declarative `[ResetOnPlayMode]` attribute controls automatic reset when entering or exiting Play Mode.
|
|
15
|
+
- **State Lifetime Control** - Inherits `DescriptionBaseSO.lifetime` (`Scenario`, `Session`, `Persistent`) for boundary-aware resets.
|
|
16
|
+
- **References** - Inspector-switchable references (`ReferenceBase<T>`) supporting both inlined values and shared variable assets.
|
|
17
|
+
- **Variable Modifiers & Aggregations** - Compose and transform variable streams dynamically.
|
|
22
18
|
|
|
23
19
|
## Installation
|
|
24
20
|
|
|
25
|
-
Install the package using npm scoped registry in `Project Settings > Package Manager > Scoped Registries
|
|
26
|
-
|
|
27
|
-
[Unity Docs - Install a UPM package from a Git URL](https://docs.unity3d.com/6000.1/Documentation/Manual/upm-ui-giturl.html)
|
|
21
|
+
Install the package using npm scoped registry in `Project Settings > Package Manager > Scoped Registries`:
|
|
28
22
|
|
|
29
23
|
```json
|
|
30
24
|
{
|
|
@@ -35,7 +29,58 @@ Install the package using npm scoped registry in `Project Settings > Package Man
|
|
|
35
29
|
"com.dbrizov.naughtyattributes"
|
|
36
30
|
]
|
|
37
31
|
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then install `cz.xprees.variables` via the Unity Package Manager.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## State Lifecycle & PlayMode Reset (DX)
|
|
39
|
+
|
|
40
|
+
Variables are completely self-contained and integrate with the centralized `StateSnapshotService` without requiring bespoke registration systems.
|
|
41
|
+
|
|
42
|
+
### 1. Default Behavior
|
|
43
|
+
|
|
44
|
+
By default, `VariableBaseSO<T>`:
|
|
45
|
+
|
|
46
|
+
1. Clones `defaultValue` into `currentValue` on `OnEnable()`.
|
|
47
|
+
2. Automatically resets to default on entering Play Mode via `[ResetOnPlayMode(PlayModeResetTiming.EnterPlayMode)]`.
|
|
48
|
+
3. In Unity Editor, captures its baseline snapshot before the first runtime mutation via `StateSnapshotService.EnsureCaptured(this)`.
|
|
49
|
+
4. Reverts cleanly to baseline upon exiting Play Mode without dirtying asset files on disk.
|
|
38
50
|
|
|
51
|
+
### 2. Customizing PlayMode Resets via `[ResetOnPlayMode]`
|
|
52
|
+
|
|
53
|
+
Use `[ResetOnPlayMode]` to customize when custom variable classes reset:
|
|
54
|
+
|
|
55
|
+
```csharp
|
|
56
|
+
using Xprees.Core;
|
|
57
|
+
using Xprees.Variables.Base;
|
|
58
|
+
|
|
59
|
+
// 1. Default: resets on Play Mode entry
|
|
60
|
+
public class AmmoVariable : IntVariable { }
|
|
61
|
+
|
|
62
|
+
// 2. Prevent Play Mode reset (e.g. for persistent editor values or external saves)
|
|
63
|
+
[ResetOnPlayMode(PlayModeResetTiming.None)]
|
|
64
|
+
public class PlayerProfileVariable : StringVariable { }
|
|
65
|
+
|
|
66
|
+
// 3. Reset on both entry and exit
|
|
67
|
+
[ResetOnPlayMode(PlayModeResetTiming.Both)]
|
|
68
|
+
public class TransientDebugFlagVariable : BoolVariable { }
|
|
39
69
|
```
|
|
40
70
|
|
|
41
|
-
|
|
71
|
+
### 3. Setting State Lifetime per Variable
|
|
72
|
+
|
|
73
|
+
Every variable ScriptableObject exposes the **Lifetime** dropdown in the Inspector (inherited from `DescriptionBaseSO`):
|
|
74
|
+
|
|
75
|
+
- **`Scenario` (Default)**:
|
|
76
|
+
Resets automatically whenever a scenario starts or restarts via `ScenarioStateRegistrySO` and `StateSnapshotService.RestoreAll()`.
|
|
77
|
+
- **`Session`**:
|
|
78
|
+
Persists across scenarios, resetting only when returning to the Main Menu or quitting.
|
|
79
|
+
- **`Persistent`**:
|
|
80
|
+
**Never reset** by scenario resets or Play Mode entry (e.g. Audio volume, resolution settings).
|
|
81
|
+
|
|
82
|
+
```csharp
|
|
83
|
+
// You can also enforce class-level lifetime via attribute:
|
|
84
|
+
[StatefulLifetime(StateLifetime.Persistent)]
|
|
85
|
+
public class MasterVolumeVariable : FloatVariable { }
|
|
86
|
+
```
|
|
@@ -3,25 +3,45 @@
|
|
|
3
3
|
// ------------------------------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
using System;
|
|
6
|
+
using UnityEngine;
|
|
6
7
|
using UnityEngine.Events;
|
|
7
8
|
using Xprees.Core;
|
|
8
9
|
using Xprees.Variables.Utils;
|
|
9
10
|
|
|
10
11
|
namespace Xprees.Variables.Base
|
|
11
12
|
{
|
|
12
|
-
/// <summary>
|
|
13
13
|
/// Base class for all variable references used in the game. It can either use an inlined value or reference a VariableBaseSO Scriptable Object. The Value property abstracts this choice away, so users of ReferenceBase don't have to care about it.
|
|
14
|
-
/// </summary>
|
|
15
14
|
/// <typeparam name="T">Unity Serializable</typeparam>
|
|
16
15
|
[Serializable]
|
|
17
|
-
public class ReferenceBase<T> :
|
|
16
|
+
public class ReferenceBase<T> : ISerializationCallbackReceiver
|
|
18
17
|
{
|
|
18
|
+
private bool _hasCapturedBaseline; // Used to check if baseline has been captured
|
|
19
19
|
private T _defaultInlinedValue; // Used to reset state of inlined value
|
|
20
20
|
|
|
21
21
|
public bool useInlined = true;
|
|
22
22
|
public T inlinedValue;
|
|
23
23
|
public VariableBaseSO<T> variable;
|
|
24
24
|
|
|
25
|
+
public void OnBeforeSerialize()
|
|
26
|
+
{
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public void OnAfterDeserialize()
|
|
30
|
+
{
|
|
31
|
+
if (!PlayModeStateTracker.IsPlaying)
|
|
32
|
+
{
|
|
33
|
+
_defaultInlinedValue = CloningTools.Clone(inlinedValue);
|
|
34
|
+
_hasCapturedBaseline = false;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!_hasCapturedBaseline)
|
|
39
|
+
{
|
|
40
|
+
_defaultInlinedValue = CloningTools.Clone(inlinedValue);
|
|
41
|
+
_hasCapturedBaseline = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
// Internal event for inlined value changes, since VariableBaseSO already has its own onValueChanged event.
|
|
26
46
|
private UnityAction<T> _onInlinedValueChanged;
|
|
27
47
|
|
|
@@ -31,7 +51,7 @@ namespace Xprees.Variables.Base
|
|
|
31
51
|
{
|
|
32
52
|
add
|
|
33
53
|
{
|
|
34
|
-
var shouldRedirectToVarEvent = !useInlined && variable
|
|
54
|
+
var shouldRedirectToVarEvent = !useInlined && variable;
|
|
35
55
|
if (shouldRedirectToVarEvent)
|
|
36
56
|
{
|
|
37
57
|
variable.onValueChanged += value;
|
|
@@ -43,7 +63,7 @@ namespace Xprees.Variables.Base
|
|
|
43
63
|
|
|
44
64
|
remove
|
|
45
65
|
{
|
|
46
|
-
var shouldRedirectToVarEvent = !useInlined && variable
|
|
66
|
+
var shouldRedirectToVarEvent = !useInlined && variable;
|
|
47
67
|
if (shouldRedirectToVarEvent)
|
|
48
68
|
{
|
|
49
69
|
variable.onValueChanged -= value;
|
|
@@ -63,6 +83,7 @@ namespace Xprees.Variables.Base
|
|
|
63
83
|
useInlined = true;
|
|
64
84
|
inlinedValue = value;
|
|
65
85
|
_defaultInlinedValue = value;
|
|
86
|
+
_hasCapturedBaseline = true;
|
|
66
87
|
}
|
|
67
88
|
|
|
68
89
|
public T Value
|
|
@@ -84,22 +105,14 @@ namespace Xprees.Variables.Base
|
|
|
84
105
|
|
|
85
106
|
public static implicit operator T(ReferenceBase<T> reference) => reference != null ? reference.Value : default;
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
_defaultInlinedValue = CloningTools.Clone(inlinedValue);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public virtual void ResetState()
|
|
108
|
+
/// Resets the inlined value to its baseline value captured at deserialization/initialization.
|
|
109
|
+
/// If this reference points to a VariableBaseSO, it does nothing (the shared variable state is managed by StateSnapshotService).
|
|
110
|
+
public virtual void ResetToDefault()
|
|
95
111
|
{
|
|
96
112
|
if (useInlined)
|
|
97
113
|
{
|
|
98
114
|
inlinedValue = CloningTools.Clone(_defaultInlinedValue);
|
|
99
|
-
return;
|
|
100
115
|
}
|
|
101
|
-
|
|
102
|
-
variable?.ResetState();
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
}
|
|
@@ -8,12 +8,6 @@ namespace Xprees.Variables.Base
|
|
|
8
8
|
[Tooltip("Aggregated variables.")]
|
|
9
9
|
[SerializeField] protected VariableBaseSO<T>[] variables;
|
|
10
10
|
|
|
11
|
-
private void OnEnable()
|
|
12
|
-
{
|
|
13
|
-
hideFlags = HideFlags.DontUnloadUnusedAsset;
|
|
14
|
-
ResetState();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
public override T CurrentValue
|
|
18
12
|
{
|
|
19
13
|
get => AggregateValue(variables);
|
|
@@ -22,15 +16,14 @@ namespace Xprees.Variables.Base
|
|
|
22
16
|
|
|
23
17
|
protected abstract T AggregateValue(VariableBaseSO<T>[] variableBases);
|
|
24
18
|
|
|
25
|
-
public override void
|
|
19
|
+
public override void ResetToDefault()
|
|
26
20
|
{
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
// Aggregations do not hold independent state; values are computed dynamically.
|
|
22
|
+
}
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
24
|
+
public override void ClearTransientState()
|
|
25
|
+
{
|
|
26
|
+
// Aggregations do not hold independent state; values are computed dynamically.
|
|
34
27
|
}
|
|
35
28
|
}
|
|
36
29
|
}
|
|
@@ -7,42 +7,46 @@ namespace Xprees.Variables.Base
|
|
|
7
7
|
{
|
|
8
8
|
public abstract class VariableBaseSO : DescriptionBaseSO
|
|
9
9
|
{
|
|
10
|
-
|
|
10
|
+
protected virtual void OnEnable()
|
|
11
11
|
{
|
|
12
12
|
hideFlags = HideFlags.DontUnloadUnusedAsset;
|
|
13
|
-
|
|
13
|
+
ResetToDefault();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
///
|
|
17
|
-
public abstract void
|
|
18
|
-
|
|
19
|
-
public override void ResetState() => ForceResetState();
|
|
16
|
+
/// Resets the variable to its authored default value.
|
|
17
|
+
public abstract void ResetToDefault();
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
///
|
|
23
|
-
///
|
|
24
|
-
///
|
|
25
|
-
/// </summary>
|
|
20
|
+
/// Base class for all variable ScriptableObjects.
|
|
21
|
+
/// Manages its own default initialization self-contained, and integrates with StateSnapshotService for scenario boundaries.
|
|
22
|
+
/// By default, resets on EnterPlayMode unless decorated with a custom [ResetOnPlayMode] or marked Persistent.
|
|
26
23
|
/// <typeparam name="T">Unity Serializable or System.Serializable</typeparam>
|
|
27
|
-
|
|
24
|
+
[ResetOnPlayMode(PlayModeResetTiming.EnterPlayMode)]
|
|
25
|
+
public class VariableBaseSO<T> : VariableBaseSO, IRuntimeStateOwner
|
|
28
26
|
{
|
|
29
|
-
[Tooltip("
|
|
27
|
+
[Tooltip("Authored default value of the variable.")]
|
|
30
28
|
[SerializeField] protected T defaultValue;
|
|
31
29
|
|
|
32
|
-
[Tooltip("Current value of variable - Runtime only value.")]
|
|
30
|
+
[Tooltip("Current value of variable - Runtime only value. Replaced with default value on OnEnable/PlayMode start.")]
|
|
33
31
|
[SerializeField] private T currentValue;
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
#if UNITY_EDITOR
|
|
34
|
+
protected virtual void OnValidate()
|
|
35
|
+
{
|
|
36
|
+
// If not playing, reset current value to default value on validate to ensure that the variable is always in a valid state in Editor.
|
|
37
|
+
if (!Application.isPlaying) currentValue = CloningTools.Clone(defaultValue);
|
|
38
|
+
}
|
|
39
|
+
#endif
|
|
40
40
|
|
|
41
41
|
public virtual T CurrentValue
|
|
42
42
|
{
|
|
43
43
|
get => currentValue;
|
|
44
44
|
set
|
|
45
45
|
{
|
|
46
|
+
#if UNITY_EDITOR
|
|
47
|
+
// If not already captured in Editor capture before changing the value
|
|
48
|
+
if (PlayModeStateTracker.IsPlaying) StateSnapshotService.EnsureCaptured(this);
|
|
49
|
+
#endif
|
|
46
50
|
currentValue = value;
|
|
47
51
|
onValueChanged?.Invoke(value);
|
|
48
52
|
}
|
|
@@ -58,13 +62,18 @@ namespace Xprees.Variables.Base
|
|
|
58
62
|
public static implicit operator T(VariableBaseSO<T> variable) =>
|
|
59
63
|
variable != null ? variable.CurrentValue : default;
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
/// Resets the variable's current value to a fresh clone of its authored default value and notifies listeners.
|
|
66
|
+
public override void ResetToDefault()
|
|
62
67
|
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
ForceResetState();
|
|
68
|
+
currentValue = CloningTools.Clone(defaultValue);
|
|
69
|
+
onValueChanged?.Invoke(currentValue);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
/// Invoked by StateSnapshotService when restoring state at scenario boundaries.
|
|
73
|
+
/// Notifies runtime listeners that the value has reverted to its snapshot baseline.
|
|
74
|
+
public virtual void ClearTransientState()
|
|
75
|
+
{
|
|
76
|
+
onValueChanged?.Invoke(currentValue);
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
}
|
|
@@ -12,13 +12,6 @@ namespace Xprees.Variables.Base
|
|
|
12
12
|
[Space]
|
|
13
13
|
[SerializeField] private BoolReference disableWrite = new(true);
|
|
14
14
|
|
|
15
|
-
private void OnEnable()
|
|
16
|
-
{
|
|
17
|
-
hideFlags = HideFlags.DontUnloadUnusedAsset;
|
|
18
|
-
disableWrite?.BackupStartState();
|
|
19
|
-
ResetState();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
public override T CurrentValue
|
|
23
16
|
{
|
|
24
17
|
get => ModifyValue(variable.CurrentValue);
|
|
@@ -36,11 +29,14 @@ namespace Xprees.Variables.Base
|
|
|
36
29
|
|
|
37
30
|
protected abstract T ModifyValue(T value);
|
|
38
31
|
|
|
39
|
-
public override void
|
|
32
|
+
public override void ResetToDefault()
|
|
33
|
+
{
|
|
34
|
+
// Modifiers do not hold independent state; state is owned by the wrapped variable.
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public override void ClearTransientState()
|
|
40
38
|
{
|
|
41
|
-
|
|
42
|
-
variable?.ResetState();
|
|
43
|
-
disableWrite?.ResetState();
|
|
39
|
+
// Modifiers do not hold independent state; state is owned by the wrapped variable.
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
using System;
|
|
2
2
|
using System.Collections;
|
|
3
3
|
using System.Runtime.CompilerServices;
|
|
4
4
|
using UnityEngine;
|
|
@@ -24,6 +24,13 @@ namespace Xprees.Variables.Utils
|
|
|
24
24
|
var canReturnAsIs = typeof(T).IsValueType && !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
|
|
25
25
|
if (canReturnAsIs) return value;
|
|
26
26
|
|
|
27
|
+
// Strings are immutable in .NET, so return directly without JsonUtility
|
|
28
|
+
if (typeof(T) == typeof(string)) return value;
|
|
29
|
+
|
|
30
|
+
// UnityEngine.Object references (ScriptableObjects, MonoBehaviours, Assets, GameObjects)
|
|
31
|
+
// are reference-assigned; we must not deep-clone or call CreateInstance during serialization.
|
|
32
|
+
if (typeof(Object).IsAssignableFrom(typeof(T)) || value is Object) return value;
|
|
33
|
+
|
|
27
34
|
if (value is ICloneable cloneable) return (T) cloneable.Clone();
|
|
28
35
|
|
|
29
36
|
// Handle collections (List<T>, arrays, etc.) - JsonUtility doesn't support them directly
|
|
@@ -96,6 +103,9 @@ namespace Xprees.Variables.Utils
|
|
|
96
103
|
// Value types are copied by value
|
|
97
104
|
if (itemType.IsValueType) return item;
|
|
98
105
|
|
|
106
|
+
// Strings are immutable
|
|
107
|
+
if (item is string) return item;
|
|
108
|
+
|
|
99
109
|
// ICloneable
|
|
100
110
|
if (item is ICloneable cloneable) return cloneable.Clone();
|
|
101
111
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cz.xprees.variables",
|
|
3
3
|
"displayName": "Variables",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.23",
|
|
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",
|
|
8
8
|
"category": "Core",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"cz.xprees.core": "1.0.
|
|
10
|
+
"cz.xprees.core": "1.0.40",
|
|
11
11
|
"com.dbrizov.naughtyattributes": "2.1.4"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|