@web-engine-dev/input
Unified input system with action mapping for web-engine-dev. Supports keyboard, mouse, gamepad, and touch input with customizable action bindings.
Features
- Action Mapping: Bind inputs to game actions
- Multi-Device Support: Keyboard, mouse, gamepad, touch
- Input Buffering: Queue inputs for responsiveness
- Composite Axes: Combine keys into axes
- Context Stacks: Switch between control schemes
- Rebinding: Runtime key remapping
Installation
bash
npm install @web-engine-dev/input
# or
pnpm add @web-engine-dev/inputQuick Start
typescript
import { ActionMapBuilder, InputManager } from '@web-engine-dev/input';
// Create input manager
const input = new InputManager();
input.initialize();
// Define action map
const actions = new ActionMapBuilder('gameplay')
.addAction('jump', 'button')
.bindKey('Space', 'jump')
.bindGamepad('a', 'jump')
.addAction('move', 'axis2d')
.bindCompositeAxis('move', {
up: 'KeyW',
down: 'KeyS',
left: 'KeyA',
right: 'KeyD',
})
.bindGamepadAxis('leftStickX', 'move')
.bindGamepadAxis('leftStickY', 'move', { invert: true })
.build();
input.registerActionMap(actions);
// Check input each frame
input.update(deltaTime);
const jump = input.getAction('jump');
if (jump?.type === 'button' && jump.justPressed) {
player.jump();
}
const movement = input.getAction('move');
if (movement?.type === 'axis2d') {
// By convention, +Y is up/forward (W/Up). For gamepad Y axes, use `invert: true`.
player.move(movement.x, movement.y);
}API Overview
Input Manager
| Method | Description |
|---|---|
initialize(config?) | Initialize input devices |
update(deltaTime) | Update input state per frame |
registerActionMap(map) | Register an action map |
unregisterActionMap(name) | Unregister an action map |
enableActionMap(name) | Enable a map |
disableActionMap(name) | Disable a map |
getAction(name) | Get current action value |
isActionTriggered(name) | Check if action triggered this frame |
onAction(name, handler) | Subscribe to action events |
onInputDeviceConnected(handler) | Subscribe to device connect events |
onInputDeviceDisconnected(handler) | Subscribe to device disconnect events |
onPointerLockChanged(handler) | Subscribe to pointer lock changes |
onInputContextChanged(handler) | Subscribe to input context changes |
getContextStack() | Get current context stack |
Bindings
typescript
// Keyboard
{ type: 'keyboard', key: 'Space' }
{ type: 'keyboard', key: 'KeyW', modifiers: { shift: true }, value: 1 }
// Axis from buttons (supports negative values)
{ type: 'keyboard', key: 'KeyW', value: 1 }
{ type: 'keyboard', key: 'KeyS', value: -1 }
{ type: 'mouseButton', button: 'left', value: 1 }
{ type: 'gamepadButton', button: 'leftTrigger', value: 1 }
// Composite axis from keys
{ type: 'compositeAxis', up: 'KeyW', down: 'KeyS', left: 'KeyA', right: 'KeyD' }
// Builder helper for composite axes
new ActionMapBuilder('movement')
.addAction('move', 'axis2d')
.bindCompositeAxis('move', { up: 'KeyW', down: 'KeyS', left: 'KeyA', right: 'KeyD' })
.build();
// Mouse
{ type: 'mouseButton', button: 'left' }
{ type: 'mouseAxis', axis: 'x', sensitivity: 0.5 }
// Gamepad
{ type: 'gamepadButton', button: 'a', gamepadIndex: 0 }
{ type: 'gamepadAxis', axis: 'leftStickX', deadzone: 0.15 }
// Touch
{ type: 'touch', gesture: 'tap', fingerCount: 1 }
{ type: 'touch', gesture: 'pan', axis: 'panX', sensitivity: 0.5 }
{ type: 'touch', gesture: 'pinch', axis: 'pinch' }
{ type: 'touch', gesture: 'rotate', axis: 'rotate', invert: true }
{ type: 'touch', gesture: 'swipe', axis: 'panX' }Context Stacks
typescript
// Push context for menus
input.pushContext('menu');
// Pop context to return
input.popContext();
// Different bindings per context
const menuActions = new ActionMapBuilder('menu-actions')
.addAction('select', 'button')
.bindKey('Enter', 'select')
.addAction('back', 'button')
.bindKey('Escape', 'back')
.build();
input.registerActionMap(menuActions);Events
typescript
// Action events
input.onAction('jump', (event) => {
if (event.phase === 'started') {
player.jump();
}
});
// Device connect/disconnect
input.onInputDeviceConnected((event) => {
console.log('Connected:', event.deviceType, event.deviceId);
});
input.onInputDeviceDisconnected((event) => {
console.log('Disconnected:', event.deviceType, event.deviceId);
});
// Pointer lock
input.onPointerLockChanged((event) => {
console.log('Pointer lock:', event.locked);
});
// Input context changes
input.onInputContextChanged((event) => {
console.log('Context change:', event.contextName, event.pushed);
});Rebinding
typescript
// Replace existing binding
input.addBindingOverride('jump', {
action: 'jump',
originalBinding: { type: 'keyboard', key: 'Space' },
newBinding: { type: 'keyboard', key: 'KeyJ' },
});
// Clear overrides
input.clearBindingOverrides('jump');Peer Dependencies
@web-engine-dev/math- Vector math for axes