Files
tts-workshop/docs/bgm/tabletop.md
T
hypercross 345832e389 docs: reorganize docs into bgm and status folders
Group the bgm spec cluster under docs/bgm and move dev logs and plans under docs/status, add an overview index, and update cross-references in the docs, README, and source comments.
2026-08-16 11:57:19 +08:00

3.6 KiB

bgm-tabletop

a r3f based interactive component library to work with bgm board games. will be used somewhere in the web app's bgm inspector routes.

1. stack

react - react, react router, tailwindv4 r3f - r3f, drei, postprocessing zustand - for state management

2. states

source-of-truth game state:

{
  surfaces: Record<string, boolean>, // enabled per surface id
  parts: Record<string, PartState>,  // part id -> placement state
}

interface PartState {
  path: string,  // the path key this part is on
  index: number, // the part's position in its path's stack
  facing: 'face' | 'back' | 'standing', // how the part is oriented on the board
}

assumption: each piece on the board has a unique id, even tokens of the same type. so a part id appears at most once, and a path's ordered children (for stacking) are derived from the map by sorting on index. this makes the render list keyed by piece id stable and unambiguous.

derived surface render state: game state + surface routes => map of piece id to { surface, route, candidate, index, stackSize, facing } for rendering on a surface. keys of this map makes a stable render list.

  • route - the matched route.
  • candidate - the matched candidate for a :param route, carrying its anchor x/y/rotation. absent for routes without candidates.
  • index - the piece's position in its path's stack.
  • stackSize - the number of pieces on the path.
  • facing - how the piece is oriented on the board (face / back / standing).

the render map is per enabled surface: a piece may appear on more than one enabled surface (e.g. an expansion path and the main board), and each is rendered independently.

3. components

  • SetupLoader side effect only component that seeds the game state with setup (enabled surfaces + part placement).
  • WorldSurfaceView mounts a surface to world space.
  • HudSurfaceView mounts a surface to hud space.
  • PartPlacement a stable per-part component that positions a part on a surface location. uses the stacking hook (below) to apply the route's stacking strategy.
  • PartView used in PartPlacement, creates a mesh from part definition. a standalone component library, so it reuses geometry/shape code from @tts/mesh rather than the web app's viewers.

4. stacking

the format's stacking strategy (curve / limit / align / steps / tilt / zStart / zEnd, see format.md §4) is implemented as a hook, e.g. useStacking(route.stacking, index, stackSize), returning the offset/rotation to apply to a piece: { x, y, rotation, z, tilt }. x/y/rotation come from the curve; z is the surface-normal height ramped from zStart to zEnd; tilt is the rotation about the card's local Y (long) axis, applied to every part. PartPlacement consumes it.

5. commands

Scripted interaction — focus, tap-to-advance, move, caption — is built on an async command layer. See commands.md for command execution (lifecycle, run contexts, tap interaction), and engine.md for the message layer above it (the queue, triggers, and orchestrators that declare and fire commands).

6. usage

  • we will inspect individual parts with PartView in the web app's part inspection route.
  • as a library, the public surface is the components above: mount a surface with WorldSurfaceView/HudSurfaceView, seed state with SetupLoader, and let PartPlacement/PartView render the pieces. the web app is one consumer; the library should not assume the web app's routes or store.
  • a surface is mounted only when enabled; a disabled surface is not rendered.