Skip to content

Tutorial: Visualizing Data with Visualizer

Take a list-shaped data source — an inventory, a tagged DataStorage, a runtime collection — and project it onto the scene as actual GameObjects: one cube per item, one card per quest, one marker per waypoint. The Visualizer component owns that pipeline: spawn, populate, arrange.

By the end of this tutorial you'll have an inventory list whose contents appear as visuals in a row at runtime, regenerating whenever items are added or removed.

Prerequisites: Familiarity with Data, LayoutComponent, and pooled creators.


How Visualizer Fits

Visualizer is the "data → scene visuals" sibling of Setter ("data → sinks") and Reactor ("data → reactive bodies"). All three are ControllableComponents that host a list of entries.

flowchart LR
    SRC["Data source<br/>(inventory, list,<br/>data storage)"]
    VE["Visualizer Entry<br/>(observes the source)"]
    SPAWN["Spawn a visual per item<br/>(via PooledObjectCreator)"]
    POP["Populate the visual<br/>(bind item data to its children)"]
    LAY["Arrange visuals<br/>(LayoutComponent or layout strategy)"]
    SRC --> VE
    VE --> SPAWN --> POP --> LAY

The entry watches a data source and keeps the scene visuals in sync — add an item, a visual appears; remove an item, a visual is reclaimed; reorder the source, the visuals rearrange.


1. Prepare a Pooled Visual

The visual is a prefab. Each item produces one instance.

  1. Create a prefab (e.g. InventoryItemVisual) representing a single visual. Give it a Renderer, a UI label, whatever you want to drive.
  2. Add a PoolObject component to the prefab root. This lets it participate in pooling.
  3. In your scene, add a PooledObjectCreator somewhere. Drop the prefab into its prefab field, set initialCount to a small pre-spawn count.

The Creator now hands out instances on demand and reclaims them when done.

Why pooled?

Visualizers add and remove visuals as the underlying data changes. Pooling avoids garbage spikes from instantiate/destroy churn.


2. Add the Visualizer

On your scene's "Inventory UI" GameObject (or any controller) add a Visualizer component.

Add Component → Jungle → Data → Visualizer

You'll see an Entries list. Each entry handles one "data → visuals" channel. We'll add one entry.


3. Configure the Entry

The available entry types depend on your data shape. A typical entry takes:

Field What it is
source The data source to observe (e.g. an inventory, a list value, a DataStorage entry).
creator The PooledObjectCreator that produces visuals.
populator How to bind one item's data to a freshly-spawned visual's children (Text fields, Renderer color, etc.).
layout How to arrange the spawned visuals (a LayoutComponent or an inline layout strategy).

Wire your inventory source, the InventoryItemVisual creator, the populator that copies each item's name/icon into the visual, and a layout strategy that arranges them in a row.

Press Play and add an item to the inventory at runtime. A new visual appears at the next layout slot. Remove an item — the visual gets reclaimed back into the pool.


4. Layout: Where the Visuals Go

Visualizers don't position the visuals themselves — they delegate to a layout. Two common surfaces:

  • Inline layout strategy on the entry — a layout strategy field (Linear, Grid, Circular, Fan, Stack) configured directly on the entry. Simpler when the layout belongs to this one Visualizer.
  • Separate LayoutComponent — a standalone layout component pointed at the visuals. Useful when several systems share the same layout.

For the row of inventory items, an inline Linear layout strategy works:

Field Value
Direction Right
Spacing 1.5
Origin This GameObject's transform

The visuals appear from the origin transform out, spaced 1.5 units apart.


5. Reorder, Remove, Replace

The Visualizer reacts to the source's change events:

  • Item added → spawn a new visual, bind it via the populator, run layout.
  • Item removed → reclaim the matching visual, run layout.
  • Source reordered → leave instances in place, run layout (visuals shift to their new slots).
  • Item replaced in place → re-populate the existing visual, don't respawn.

The mapping from item to visual is identity-based — the Visualizer keeps a dictionary, so the same item keeps its same visual instance across reorderings.


6. Visualize a Plain List

The Visualizer accepts any source that exposes a "list-shaped" interface — most commonly:

  • An inventory (a list of typed items).
  • A DataStorage with an "Items" key (list of GameObjects, list of typed values).
  • A Detector mirrored through IItemSource<GameObject> — visuals follow detected objects.

Pick the source type that matches your data and wire it the same way.


7. Per-Entry Lifecycle

Each entry begins and ends with the Visualizer. On End:

  • Every active visual is returned to its creator's pool.
  • The internal item-to-visual map clears.
  • The next Begin re-snapshots the source and respawns fresh visuals.

This means turning the Visualizer off (or its execution frame ending) is a hard reset — anything in-flight ends cleanly.


8. Multiple Entries in One Visualizer

You can host many entries in one Visualizer for grouped "data → visuals" pipelines that share a lifecycle:

Visualizer
├─ Entry 1: Inventory items → row of cards
├─ Entry 2: Quest list → vertical sidebar
└─ Entry 3: Waypoint markers → world-space pins

All three start when the Visualizer begins, all three end together.

For independent lifecycles, use separate Visualizer components.


9. Visualizer vs Setter vs Reactor

Component Direction Typical sink
Setter Data → typed values / DataStorage Settable values, keys, ref fields.
Reactor Data → reactive bodies Process bodies that react to triggers.
Visualizer Data → scene visuals Pooled GameObjects with bound children, arranged by a layout.

All three are siblings — same lifecycle host, same execution-frame model, picked based on what kind of consumer you need on the right side.


Next Steps