FERRETGame engine over Babylon.js
Ferret

@ferret/input

Actions over the console's buttons and sticks, keyboard and mouse, touch and gamepads; games bind actions, not devices.

10 exports · source in packages/input/src

Classes

InputService class

class InputService implements Service

Actions over every input device. Games bind actions, not devices. The active kind follows what the player uses last (a gamepad plugged in, a keyboard used on a tablet) and prompts show the matching button.

readonly name: "input"
kind: InputKind
enabled: booleanfalse = all actions read as released/zero (a menu is open, a cutscene plays). Devices are still tracked.
readonly opts: InputOptions
dominantHand: XrHandwhich hand xrButton without hand means
canvas?: HTMLElementthe page's canvas, once attached; Pointer Lock is requested on it
constructor(opts: InputOptions)
init(world: World)
down(action: string)
pressed(action: string)
released(action: string)
axis(action: string): { x: number; y: number }
lookDelta()Look delta accumulated during the last step, in radians (x = yaw, y = pitch). Consumed by the step.
prompt(action: string): stringThe label of the binding in the active kind ("A", "E", "Tap"...), for UI prompts.
setKind(kind: InputKind)
setKey(code: string, down: boolean)
addLook(dx: number, dy: number)
setPad(buttons: Iterable<number>, axes: number[])
setTouchStick(which: 'left' | 'right', x: number, y: number)
setTouchButton(name: string, down: boolean)
setXrActive(on: boolean)The xr kind while in a session; the previous kind comes back on leaving.
setXrButton(hand: XrHand, b: XrButton | 'pinch', down: boolean)
setXrStick(hand: XrHand, x: number, y: number)
setXrPose(who: 'head' | XrHand, p: XrPose)Poses sampled for the rendered frame (renderPose); the fixed step copies them for gameplay (pose).
pose(who: 'head' | XrHand): XrPose | undefined
renderPose(who: 'head' | XrHand): XrPose | undefined
inject(action: string, v: { x?: number; y?: number; down?: boolean } | null)Tests and scripted input: force an action regardless of devices.
fixedUpdate()Called before the components' fixed update, at the fixed rate.
sample()Compute the actions from the raw state. Called each fixed step; also callable directly in tests.
attach(canvas: HTMLElement, opts: { pointerLock?: boolean } = {})Listen to the page. On OS Beroun the cursor, A and B arrive as keys; Pointer Lock is only used in desktop browsers.
update()Poll the Gamepad API; call once per frame (the Ferret loop does it through update).
dispose()

packages/input/src/input.ts:46

TouchControls class

class TouchControls

On-screen controls for phones and tablets, generated from the game's touch bindings: a virtual stick on the left, look by dragging on the right half, and a round button per touchButton binding. Sized to the screen.

readonly root: HTMLDivElement
constructor(private input: InputService, parent: HTMLElement = document.body)
relayout()
dispose()

packages/input/src/touch.ts:10

Interfaces

InputOptions interface

interface InputOptions
actions: Record<string, ActionDef>
bindings: BindingMap
defaultKind?: InputKindKind used until the player touches something. OS Beroun: 'console', phones: 'touch', else 'keyboard'.

packages/input/src/input.ts:29

XrPose interface

interface XrPose

a hand or the head in the room: metres, quaternion, m/s, rad/s

position: [number, number, number]
rotation: [number, number, number, number]
linear: [number, number, number]
angular: [number, number, number]
tracked: boolean

packages/input/src/input.ts:9

Types

ActionDef type

type ActionDef = { kind: 'button' } | { kind: 'axis2' }

packages/input/src/input.ts:11

Binding type

type Binding = | { key: string; label?: string } // KeyboardEvent.code (or .key for console) | { mouse: 0 | 1 | 2; label?: string } // a mouse button: 0 left, 1 middle, 2 right | { axisKeys: [left: string, right: string, down: string, up: string]; label?: string } // x = right - left, y = up - down | { pad: number; label?: string } // Gamepad button index | { padAxes: [x: number, y: number]; deadzone?: number } | { mouseLook: true; sensitivity?: number } // pointer-locked (or dragged) mouse -> axis2 in radians | { touchStick: 'left' | 'right' } | { touchButton: string; label?: string } | { xrButton: XrButton; hand?: XrHand; label?: string } // a controller button; no hand = the dominant hand | { xrStick: XrHand; deadzone?: number } // a controller thumbstick -> axis2 | { xrPinch: XrHand; label?: string }

One way to produce an action. Which button does what is each game's call: Ferret ships no mapping.

packages/input/src/input.ts:14

BindingMap type

type BindingMap = Partial<Record<InputKind, Record<string, Binding[]>>>

packages/input/src/input.ts:27

InputKind type

type InputKind = 'console' | 'keyboard' | 'gamepad' | 'touch' | 'xr'

Where the player's commands come from. console = OS Beroun's cursor, A and B (they reach the page as arrow keys, Enter and Escape).

packages/input/src/input.ts:4

XrButton type

type XrButton = 'trigger' | 'squeeze' | 'thumbstick' | 'a' | 'b' | 'x' | 'y' | 'menu'

packages/input/src/input.ts:6

XrHand type

type XrHand = 'left' | 'right'

packages/input/src/input.ts:7