DocsComponents

Shapes, behaviors & value bindings

Beyond the kind-specific fields, every componentpiece, card, die, token — shares a set of optional rendering refinements. Each one defaults to today’s plain behaviour when absent, so you only add them when an object needs a non-rectangular outline, snap rotation, an attached marker, or a live number.

These are still table-as-scene fields: they change how an object is drawn and handled, not what is legal. In particular a value’s min / max / step are display hints — taybl never clamps the number.

How to edit in the Studio

Open your game → Layout tab and select the component. The Behaviors section exposes the Flippable, Rotatable, and Stackable toggles. The finer refinements — shape, footprint, rotationStep / orientations, attachable, and valueBinding — are produced by the generation pipeline into the GDL and don’t have a dedicated inspector control yet; adjust them by editing the generated definition.

Shape — the outline within the size box

A component’s shape is the outline drawn inside its size bounding box — it sets the hit-area, the colour fallback, and how tiles tessellate. It is a discriminated union on type. Absent → rect.

typeExtra fieldsWhat it draws
rectA rectangle (the default)
circleA circle with diameter min(w, h)
hexorientation: pointy or flatA hexagon, point-up or flat-top
polygonpoints: array of { x, y } (≥ 3)A free outline; each point is 0..1 of the size box (0,0 = top-left, 1,1 = bottom-right) — used for meeple silhouettes

Footprint — multi-cell pieces

A footprint lets one component cover several grid cells (a domino, a polyomino, a 2×2 building). It is { cells: { col, row }[] } — integer offsets from the anchor cell, which is the cell a placement or move names. The anchor { col: 0, row: 0 } must be present; the covered cells are derived at render time, so no runtime state changes. Absent → the component occupies a single cell.

Behaviors — how a component can be handled

The behaviors object toggles the drag affordances on a component. The first three are checkboxes in the inspector:

FieldValuesDefaultWhat it does
flippabletrue.optional()The component can be turned over (front ↔ back)
rotatabletrue.optional()The component can be rotated
stackabletrue.optional()Copies can be stacked into a pile
rotationStepnumber > 0 (degrees).optional()When rotatable, the snap increment (e.g. 90 → four orientations). Absent → free rotation. Ignored whenever orientations is present
orientationsarray of angles in degrees.optional()Explicit allowed angles (e.g. [0, 90, 180, 270]). When present this is the full set of legal angles and overrides rotationStep. Each entry is wrapped into [0, 360) and de-duplicated

Attachable — pinned markers

attachable: true lets a component be pinned onto another object instance — a +1/+1 counter on a creature, a damage marker on a card. A pinned marker follows its host when it moves and is removed or captured with it; the engine handles that ripple, and the renderer draws the marker as an overlay on the host. Absent → not attachable.

FieldValuesDefaultWhat it does
attachabletrue.optional()This component may be pinned onto another object instance

Attach slots — named positions on a host

When a host component declares attachSlots, markers that attach to it can target a specific slot instead of the generic anchor (corner / center / edge). Each slot has fractional x / y coordinates within the host's bounding box (0,0 = top-left, 1,1 = bottom-right), an optional label, an accepts filter, and a capacity (default 1). The renderer draws the marker centred at the slot's position. When no slotId is provided on attach, the engine auto-assigns the first slot with available capacity.

FieldValuesDefaultWhat it does
attachSlots[].idtext (snake_case)Unique slot identifier within this component
attachSlots[].labeltext.optional()Display name shown on hover
attachSlots[].x0–1Horizontal position as a fraction of the host width
attachSlots[].y0–1Vertical position as a fraction of the host height
attachSlots[].acceptsarray of kinds.optional()Which component kinds may attach here (omit = any)
attachSlots[].capacityinteger > 01Maximum markers this slot holds

Value binding — a live number on a component

A valueBinding renders a runtime number on the component — a life total, a money count, a dial. The renderer reads key from either global or the owning player’s state. As with every taybl field, the bounds are advisory: min / max / step shape the spinner widget but the engine does not enforce them. Absent → no number is drawn.

FieldValuesDefaultWhat it does
scopeglobal playerWhether the number lives on the table or on the owning player
keytextWhich state value to read and display
displaynumber dial.optional()A numeric badge (default) or a dial widget
minnumber.optional()Display-only lower bound for the spinner — not enforced
maxnumber.optional()Display-only upper bound for the spinner — not enforced
stepnumber > 0.optional()Spinner increment size — display hint only
  • Components overview — the shared fields and kinds
  • Tokens — the most common home for a valueBinding counter
  • Pieces — meeple silhouettes via shape: polygon
  • Grids — the cells a footprint covers
Sign in to ask Pip about building games.
Sign in