Skip to content

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:

  1. A Measurer component on a GameObject. It owns one DataStorage and runs a list of measurement sub-setters against it each tick.
  2. A targeted measurer entry (e.g. AttachableMeasurements, AttachmentPointMeasurements, or the generic TargetedMeasurer). Resolves which GameObject / component to measure.
  3. 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 CubeMotionStorage asset 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 SmartAttachableObject target. Use when measuring an Attachable.
  • Attachment Point Measurements — resolves a SmartAttachmentPoint target.
  • 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 Attached cycle.
  • 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