Tutorial: Typed Key-Value Storage with DataStorage¶
Store and look up runtime values by Key — gameplay stats, UI state, save data, anything that lives as named typed entries. The DataStorage system gives you typed access to a shared key-value bag without coupling readers to writers.
By the end of this tutorial you'll have a PlayerStats storage with health, score, and a current weapon, accessed safely from multiple unrelated systems.
Prerequisites: None — this is foundational.
What Is a DataStorage?¶
Two kinds of host:
- DataStorageAsset — a
ScriptableObjectasset. Shared by everything that references it. Useful for "global player stats", project-wide settings. - DataStorageComponent — a
MonoBehaviourinstance. Lives on a specific GameObject. Useful for "per-NPC stats", per-instance data.
Both wrap a DataStorage instance — a typed dictionary keyed by Key ScriptableObjects.
Each entry is stored under a typed Key. Reading a key returns its current value; writing sets it.
1. Create the Storage¶
Right-click in the Project window → Create → Jungle → Data → Data Storage. Name it PlayerStats.
The asset's Inspector shows its current entries (empty for now). Entries are added at edit time or runtime.
2. Create Keys¶
Each typed slot needs a Key. Keys are ScriptableObjects — refactor-safe identifiers.
- Create → Jungle → Data → Key named Health.
- Repeat for Score and CurrentWeapon.
The Key itself has no type — the type comes from how it's used (whichever typed source/sink references it). The same Key can be reused across multiple storages if you want.
3. Author Initial Values¶
In the PlayerStats asset, add entries:
| Key | Type | Initial value |
|---|---|---|
| Health | float | 100 |
| Score | int | 0 |
| CurrentWeapon | GameObject | (reference to default weapon prefab) |
Save the asset.
4. Read a Key¶
Any "value from key" provider can read from the storage. Common ones:
| Source | What it reads |
|---|---|
FloatFromDataStorage |
One float entry. |
IntFromDataStorage |
One int entry. |
BoolFromDataStorage |
One bool entry. |
GameObjectFromDataStorage |
One GameObject entry. |
ObservableFloatValueFromKey |
One float entry with change notifications. |
Drop one of these into any SmartFloat, SmartInt, etc. field. Set its storage to the PlayerStats asset, its key to the Health Key. The field now reflects the live value.
Conditions can read too — a FloatValueCondition reading Health < 25 becomes "low health".
5. Write a Key¶
Write via:
- A Process Component running a Value Command: add Write Float (once) or Set Float Over Time (gradually), and pick Float From Data Storage pointed at the key as its sink. See the Writing Values tutorial.
- A Measurement Command: each measurement, such as Speed, writes into its own sink. Point the sink at the key.
- A DataStorageCommand — for one-off ops triggered by other processes (set, increment, clear).
- Direct code —
dataStorage.SetFloat(key, value).
The point is: every reader/writer agrees on the Key. Multiple writers / multiple readers, all decoupled.
6. Local vs Global Storage¶
Asset (DataStorageAsset):
- Persists between scene loads.
- Shared by every GameObject that references it.
- Edit-time values become runtime starting values.
- Important: edits to the asset during play mode are preserved into edit mode unless you reset. To reset, add a Process Component that starts with the scene and runs a
DataStorageCommandwith a Clear operation.
Component (DataStorageComponent):
- Per-instance.
- The Inspector shows the live values during play, scoped to that GameObject.
- Resets automatically when the GameObject is disabled and the scene reloads.
Pick the asset when you want "one game, one stats bag". Pick the component when each prefab instance has its own.
7. Item Providers¶
An ItemProvider component assigns an Item asset to a GameObject — so inventory operations know which item a scene object represents when storing or retrieving it. Add it to prefabs that participate in inventory systems.
8. Listening for Changes¶
Wrap the read in an Observable source — ObservableFloatValueFromKey, ObservableIntValueFromKey, etc. — and pass it to:
- A Reactor target for typed reactive bodies.
- A UI binder that updates a label on change.
- A Condition that polls.
The observable fires only on actual change, not every frame.
9. Composite Values from Multiple Keys¶
To fold several keys into one value, pick a multi-key provider and give it a combiner:
| Provider | Combiners | What you get |
|---|---|---|
Multi-Key Float (MultiKeyFloat) |
Sum, Min, Max, Average | One number from several float keys |
Multi-Key Int (MultiKeyInt) |
Sum, Min, Max | One number from several int keys |
Multi-Key Bool (MultiKeyBool) |
All True, Any True, All False, None True | AND / OR over several bool keys |
Bool Keys As Int (BoolKeysAsInt) |
Count True, Count False | How many flags are set |
Drop the provider in any observable slot, list the keys, pick the combiner. The result stays observable, so a reaction on it fires whenever any of those keys changes — no code, and nothing to keep in sync.
Thresholds are not combiners
"Is this at least 100" and "is this within a band" are not derived values; they are triggers. Put a Threshold or Range trigger on the reaction watching the value, or a FloatValueCondition / IntValueCondition when a command needs to gate on it.
10. Common Patterns¶
Health bar¶
- Storage:
PlayerStats. - Key:
Health(float). - Writer: A
DataStorageCommandwithSetFloatops, fired by damage / heal events. - Reader: UI binder →
ObservableFloatValueFromKey(PlayerStats, Health)→ fill amount.
Inventory count¶
- Storage:
PlayerStats. - Key:
Coins(int). - Writer:
IncrementIntOperationon pickup events. - Reader: an
IntValueConditionon theCoinskey gates the "buy" button.
Save game¶
- Storage: any number of
DataStorageAssets. - Reader: at game start, hydrate UI / world state from each key.
- Writer: at game end / interval, snapshot the current values into a save file.
Next Steps¶
- Writing Values tutorial: writing into DataStorage.
- Reactor tutorial — reacting to DataStorage changes.
- Measurement tutorial: writing measurements into keys.
- Data overview — system context.
- DataStorageAsset / DataStorageComponent references.