GameStateVariable (and its subtypes GameStateNumberVariable / GameStateBoolVariable) are ScriptableObject initialValue and a key. They do not hold the live, current value.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.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 updates the dictionary onChanged EventSlot in the same call - there's no separate notify step. Reading via GetValue never fires anything.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.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.GameStateManager.instance - the singleton (via Unique<T>)..GetValue( GameStateNumberVariable ) / .GetValue( GameStateBoolVariable ).SetValue( variable, value ) - updates + fires onChanged.ToJson() / .FromJson( json )GameStateNumberVariable: a new GameStateVariable subclass, a new dictionary + Get/SetValue overload pair on GameStateManager, and a new Entry class for JSON.