Tutorial: Measuring Motion¶
Track speed, distance, acceleration, angular velocity, bounds — anything spatial — on Attachables, AttachmentPoints, or any GameObject. Write the measurements into a DataStorage by keys, then read them anywhere: conditions, UI bindings, reactors, save state.
By the end of this tutorial you'll have a cube whose live speed and traveled distance are stored under Keys, and a Reactor that fires when speed crosses a threshold.
Prerequisites: A scene with a moving Cube (an AttachableObject driven by an AttachingAgent is a good baseline). Basic familiarity with DataStorage helps.
How Measurements Fit Together¶
Three pieces in play:
- A Measurer component on a GameObject. It owns one
DataStorageand runs a list of measurement sub-setters against it each tick. - A targeted measurer entry (e.g.
AttachableMeasurements,AttachmentPointMeasurements, or the genericTargetedMeasurer). Resolves which GameObject / component to measure. - Individual measurements under the targeted entry —
SpeedMeasurement,PositionMeasurement,DistanceTraveledMeasurement, etc.
flowchart LR
Measurer["Measurer component<br/>(owns one DataStorage)"]
Tgt["Targeted Measurer<br/>(resolves subject)"]
M1["SpeedMeasurement"]
M2["DistanceTraveledMeasurement"]
DS["DataStorage<br/>(keyed values)"]
Measurer --> Tgt
Tgt --> M1
Tgt --> M2
M1 -->|writes 'Speed' key| DS
M2 -->|writes 'DistanceTraveled' key| DS
Once the values land in DataStorage, anything that reads from a Key picks them up.
1. Create the DataStorage¶
Right-click in the Project window → Create → Jungle → Data → Data Storage. Name the asset CubeMotionStorage.
Alternatively, put a DataStorageComponent on the GameObject for an instance-scoped storage.
2. Make a Few Keys¶
Each measurement writes to a Key ScriptableObject. Create:
- Create → Jungle → Data → Key — name it Speed.
- Repeat for DistanceTraveled and Position.
Many measurement types ship with a default key attribute that auto-assigns a sensible Key when you drop the measurement into a Measurer. You can still override it.
3. Add a Measurer¶
On the Cube (or any controller GameObject), add a Measurer component.
Add Component → Jungle → Data → Measurer
- measurementsStorage — drag the
CubeMotionStorageasset in (or the DataStorageComponent on the same object).
4. Add a Targeted Measurer¶
Click + on the Measurer's measurers list. The picker shows ITargetedMeasurer implementations:
- Attachable Measurements — resolves a
SmartAttachableObjecttarget. Use when measuring an Attachable. - Attachment Point Measurements — resolves a
SmartAttachmentPointtarget. - Targeted Measurer — generic GameObject target. Use for anything else.
For this tutorial pick Attachable Measurements and set the target's SmartAttachableObject to the cube (Local mode, drag the cube's AttachableObject in).
5. Add Measurements¶
Inside the targeted measurer, click + on its measurements list. The picker shows:
| Measurement | What it writes |
|---|---|
PositionMeasurement |
World position vector. |
VelocityMeasurement |
Velocity vector (position delta / time). |
SpeedMeasurement |
Scalar speed. |
AccelerationMeasurement |
Acceleration vector. |
AngularVelocityMeasurement |
Rotation rate. |
DistanceTraveledMeasurement |
Cumulative distance moved since Begin. |
BoundsMeasurement |
World-space bounds. |
Add SpeedMeasurement and DistanceTraveledMeasurement. Each shows a Key field — the default keys should already match the assets you created (Speed, DistanceTraveled). Override if needed.
Press Play and drag the cube. Open the DataStorage inspector / Jungle Debug panel — you'll see Speed and DistanceTraveled updating in real time.
6. Read the Measurements Elsewhere¶
The measurements are now plain DataStorage entries — anything that takes an observable value can read them.
Display in UI¶
Bind a UI label to the Speed Key via the standard ObservableFloatValueFromKey + a UI binder.
React to a Threshold with a Reactor¶
Add a Reactor component. Click + on its targets and pick the Float target. Configure:
| Field | Value |
|---|---|
| Source | ObservableFloatValueFromKey reading the Speed key from CubeMotionStorage. |
| Trigger | ThresholdFloatTrigger — Above 5.0. |
| Body | Whatever you want to run when the cube is moving fast — e.g. spawn a trail particle, play a whoosh sound, raise a signal. |
Now the Reactor fires when the cube's speed climbs above 5, and stops when it drops back. Hysteresis on the trigger prevents flutter near the boundary.
7. Lifecycle: When Are Measurements Active?¶
The Measurer is a ControllableComponent — it runs while the host frame is in begin state.
- Default frame: on enable / on disable. Measurements update while the GameObject is active.
- For "measure only while attached": swap to a Targeted Cycle Frame subscribed to the Attachable's
Attachedcycle. - For "measure only during a specific phase": tie it to whichever phase or signal makes sense.
When the Measurer ends, DistanceTraveledMeasurement resets to 0; instantaneous measurements simply stop updating.
8. Reset-on-Play¶
DataStorage values are part of the asset by default — they persist between play sessions and between scene reloads. For measurements you usually don't want that.
To clear an asset at play start, add a DataStorageCommand with a Clear DataStorage operation, and wire it to your scene's start event (or a Setter's begin-once execution scheduler).
See the DataStorage docs for the full reset patterns.
9. Authoring Custom Measurements¶
The shipped measurement set covers most needs. To add a new one, implement IMeasurement (or the matching targeted-measurer base) and write the value into the storage on each tick. Mark the class with [JungleClassInfo] and [TypeHelp] so designers see it in the picker.
Next Steps¶
- Data system overview — DataStorage + Keys.
- Reactor reference — running responses on measurement changes.
- Setter reference — Measurer is a flattened Setter for the common DataStorage case.
- Measurer reference — full property list.