Skip to content

Declare movement as a vector action, bound per device and merged by Controls - #65

Merged
SandroMaglione merged 1 commit into
mainfrom
feat/vector-actions
Oct 23, 2026
Merged

SandroMaglione merged 1 commit into
mainfrom
feat/vector-actions

Conversation

@SandroMaglione

Copy link
Copy Markdown
Member

Why

Movement is the one input every game has, and the one input Controls didn't model. Games split it into four button actions and rebuilt the direction by hand (normalizeMovement in top-down). That meant:

  • the code was tied to a device name;
  • the touch stick was never read;
  • nothing checked that a device could move the player;
  • every game re-decided diagonal speed and how a stick and keys combine.

What

  • Declaration (breaking): Controls.actions({ buttons, vectors }). A name declared as both kinds doesn't compile.
  • Bindings:
    • Keyboard.vector({ up, down, left, right }): opposite keys cancel, diagonals have length 1.
    • Gamepad.vector({ stick, up, down, left, right }): a stick, the d-pad, or both; the longer wins.
    • A Touch layout's stick.vector names the vector the stick drives.
  • Snapshots: every device snapshot has vectors; Pointer's is empty.
  • Merge:
    • Controls takes the longest vector any device reports, so devices never add up past 1 or cancel each other.
    • Vectors work with lacking, available, idle and lastUsed.
    • Compile errors cover: a vector bound as a button, a button bound as a vector, a missing vector, and reading the wrong kind.
  • Rebind: rebind and parseRebinding take vector bindings; Gamepad can swap sticks.
  • Timelines (breaking for keyboard): keyboard timelines name directions ("move.right"). Gamepad and touch timelines are unchanged.
  • Touch (breaking): Touch.idle({ buttons, vector? }) and Touch.scripted({ buttons, vector? }, timeline).
  • Callers:

Checks

  • pnpm run check (652 tests, with type tests for every error above)
  • pnpm pack:check
  • pnpm --dir website build
  • No runtime internals or core types changed; the browser package has no bench cases.

🤖 Generated with Claude Code

…ontrols

Controls.actions({ buttons, vectors }) declares vector actions next to
buttons. Keyboard.vector and Gamepad.vector bind one to keys, a stick, or
the d-pad, and a Touch layout's stick names the vector it drives; every
snapshot reports vectors with length at most 1. Controls merges the longest
and checks kinds, gaps, and lacking for vectors like buttons. Keyboard
timelines name directions ("move.right"). Top-down drops normalizeMovement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@SandroMaglione
SandroMaglione merged commit 56c2d83 into main Oct 23, 2026
3 checks passed
@SandroMaglione
SandroMaglione deleted the feat/vector-actions branch October 23, 2026 03:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant