Game State

Assets/Scripts/Rokojori/GameState - the shared state layer everything else reads and writes through. See also outline/tech/index.page for the design intent.

The Split: Asset vs Runtime Value

This is the one non-obvious thing about this system. GameStateVariable (and its subtypes GameStateNumberVariable / GameStateBoolVariable) are ScriptableObject assets - shared, on-disk config. They only hold an initialValue and a key. They do not hold the live, current value.

The actual mutable game state lives in two dictionaries on GameStateManager - _numberValues and _boolValues, both marked [NonSerialized] since they're pure runtime cache, rebuilt from the variables list in Awake(). Never read or write these dictionaries directly - always go through GameStateManager.instance.GetValue(...) / SetValue(...), which are overloaded per variable type.

key vs name

ScriptableObjects already have a name (inherited from UnityEngine.Object - the asset's filename). GameStateVariable.key is a separate, deliberately-added field used as the stable identifier in JSON save data, so renaming an asset in the editor doesn't silently break old save files.

SetValue Also Fires onChanged

SetValue updates the dictionary and dispatches the variable's own onChanged EventSlot in the same call - there's no separate notify step. Reading via GetValue never fires anything.

Save / Load

ToJson() / FromJson( json ) use Unity's JsonUtility, which can't serialize a Dictionary directly - hence the private NumberEntry / BoolEntry / SaveData classes that flatten the dictionaries into lists of key/value pairs first.

Gotcha: if FromJson finds a saved key that no longer matches any variable in the current variables list, that entry is silently skipped - no error, no warning.

Cheat Sheet

GameStateManager.instance - the singleton (via Unique<T>).
.GetValue( GameStateNumberVariable ) / .GetValue( GameStateBoolVariable )
.SetValue( variable, value ) - updates + fires onChanged
.ToJson() / .FromJson( json )

To add a new variable type (e.g. string), mirror GameStateNumberVariable: a new GameStateVariable subclass, a new dictionary + Get/SetValue overload pair on GameStateManager, and a new Entry class for JSON.