Skip to content

Read player input ​

Goal ​

Declare named actions once; read them on keyboard, gamepad and touch.

Files you will edit ​

  • src/game.ts — declare bindings and read input.

Steps ​

  1. Bind buttons and axes in defineGame:

    ts
    import { defineGame } from 'gameable';
    
    const feedback = { fired: 0 };
    
    export default defineGame({
      actions: {
        fire: { keys: ['Space'], gamepad: [7], touch: 'tap' },
        reload: { keys: ['R'], gamepad: [2], touch: 'button:RELOAD' },
        move: { axis2: ['A', 'D', 'S', 'W'], stick: 'left', gamepadAxes: [0, 1] },
      },
      systems: [
        (ctx) => {
          const move = ctx.input.axis2('move');
          if (ctx.player) ctx.physics.moveCharacter(ctx.player, move.x * 5, 0, -move.y * 5);
          if (ctx.input.action('fire').pressed) {
            feedback.fired = ctx.frame;
            ctx.hud.set(feedback);
          }
        },
      ],
    });

    Read pooled { down, pressed, released } immediately. Any binding can supply an edge. Standard gamepad indices: RT 7, X 2. Touch: tap, hold, doubleTap, or button:<label> (up to 32 automatic touch buttons). Enable the host input module; touch: false disables touch. Mouse aliases LMB, RMB, MMB exclude the touch look finger.

    Axes combine [-X, +X, -Y, +Y] keys, a touch stick (default left) and gamepadAxes: y forward, radial 0.15 deadzone, unit-clamped. Host touch sticks: touch: { sticks: 'both' }, overlay: true.

  2. Run gameable check. Each declared button needs keyboard, gamepad and touch bindings. Intentionally keyboard-only? Declare { keys: ['Space'], devices: ['keyboard'] }. devices also restricts runtime bindings. The checker reads literals and same-file constants; computed declarations receive a warning to verify coverage manually.

  3. Use the low-level API when needed:

    pressed(key) / released(key) are step edges; isDown(key) is held. Keys accept codes (KeyW), aliases (W) and gestures (Tap, Hold). Taps pulse both edges without held state. gamepad(index) returns a pad or null; buttons, pressed, released are masks (1 << buttonIndex). mouse exposes x/y, dx/dy, wheel and locked; readers take masks. Touch also drives the pointer. Pointer lock belongs to the host.

    axis2('A', 'D', 'S', 'W') returns pooled x/y, merging WASD, arrows, left touch and first physical pad; page control overrides it. The fifth argument selects left, right or none. Other keys merge only keys and touch.

Verify ​

Run npm run dev. Test movement with keys and both left sticks. Space, RT or a tap fires; R, X or the RELOAD touch button reloads. Run gameable check.

See also ​