InputManager
InputManager simplifies input handling in multiplayer Phaser games. It automatically maps keyboard/pointer input to game actions, handles debouncing, and supports both continuous and one-shot inputs.
Quick Start
// Create input manager
const inputManager = adapter.createInputManager();
// Bind keys
inputManager.bindKeys({
'W': { action: 'move', input: { y: -1 } },
'S': { action: 'move', input: { y: 1 } },
'Space': 'jump' // Shorthand
});
// Update in game loop
update() {
inputManager.update();
} API Reference
class InputManager {
bindKeys(bindings: KeyBindings): void;
bindKeysAggregated(action: string, keyMap: Record<string, string>, options?: AggregateOptions): void;
bindCursors(cursors: Phaser.Types.Input.Keyboard.CursorKeys, bindings: CursorBindings): void;
loadProfile(profileName: string, options?: ProfileOptions): void;
update(): void;
}
interface KeyBindings {
[key: string]: ActionBinding | string;
}
interface ActionBinding {
action: string;
input?: any;
mode?: 'continuous' | 'oneshot';
targetId?: string;
} Binding Keys
bindKeys()
Map individual keys to actions.
bindKeys(bindings: KeyBindings): void Example:
inputManager.bindKeys({
// Full binding
'W': {
action: 'move',
input: { y: -1 },
mode: 'continuous' // Fires every frame while held
},
'S': {
action: 'move',
input: { y: 1 },
mode: 'continuous'
},
// One-shot (fires once on press)
'Space': {
action: 'jump',
mode: 'oneshot'
},
// Shorthand (defaults to oneshot, no input)
'E': 'interact',
'R': 'reload'
}); bindKeysAggregated()
Aggregate multiple keys into a single input state. Perfect for platformers and twin-stick shooters.
bindKeysAggregated(
action: string,
keyMap: Record<string, string>,
options?: {
initialState?: Record<string, any>;
mode?: 'continuous' | 'oneshot';
targetId?: string;
}
): void Example:
// Platformer movement
inputManager.bindKeysAggregated('move', {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'Space'
});
// Automatically tracks: { left: true/false, right: true/false, up: true/false }
// Top-down movement (WASD)
inputManager.bindKeysAggregated('move', {
left: 'A',
right: 'D',
up: 'W',
down: 'S'
});
// Twin-stick shooter (move with WASD, aim with arrows)
inputManager.bindKeysAggregated('move', {
left: 'A',
right: 'D',
up: 'W',
down: 'S'
});
inputManager.bindKeysAggregated('aim', {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'ArrowUp',
down: 'ArrowDown'
}); bindCursors()
Bind Phaser’s cursor keys.
bindCursors(
cursors: Phaser.Types.Input.Keyboard.CursorKeys,
bindings: CursorBindings
): void Example:
const cursors = this.input.keyboard.createCursorKeys();
inputManager.bindCursors(cursors, {
left: { action: 'move', input: { x: -1 } },
right: { action: 'move', input: { x: 1 } },
up: { action: 'move', input: { y: -1 } },
down: { action: 'move', input: { y: 1 } },
space: 'jump',
shift: 'dash'
}); Input Modes
Continuous
Fires every frame while key is held. Perfect for movement.
inputManager.bindKeys({
'W': {
action: 'move',
input: { y: -1 },
mode: 'continuous' // Default
}
});
// Submits action every frame while W is held One-shot
Fires once when key is pressed (debounced). Perfect for jumps, attacks.
inputManager.bindKeys({
'Space': {
action: 'jump',
mode: 'oneshot'
}
});
// Only fires once per key press, not every frame Input Profiles
Pre-configured input schemes for common game types.
loadProfile()
Load a predefined input profile.
loadProfile(profileName: string, options?: ProfileOptions): void Available profiles:
'platformer'- Arrow keys + Space'topdown'- WASD + Space'twinstick'- WASD move + Arrow aim + Space shoot
Example:
// Basic platformer controls
inputManager.loadProfile('platformer');
// With custom options
inputManager.loadProfile('platformer', {
jumpKey: 'W', // Use W instead of Space
shootKey: 'Space', // Add shoot
interactKey: 'E' // Add interact
});
// Top-down shooter
inputManager.loadProfile('topdown');
// Twin-stick shooter
inputManager.loadProfile('twinstick'); Complete Examples
Platformer
import { PhaserAdapter } from '@martini-kit/phaser';
class GameScene extends Phaser.Scene {
private inputManager!: InputManager;
create() {
const adapter = new PhaserAdapter(runtime, this);
this.inputManager = adapter.createInputManager();
// Aggregated movement input
this.inputManager.bindKeysAggregated('move', {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'Space' // Jump
});
// Additional actions
this.inputManager.bindKeys({
'E': 'interact',
'R': { action: 'restart', mode: 'oneshot' }
});
}
update() {
// Process input
this.inputManager.update();
}
} Top-Down Shooter
class GameScene extends Phaser.Scene {
create() {
const adapter = new PhaserAdapter(runtime, this);
this.inputManager = adapter.createInputManager();
// Movement (WASD)
this.inputManager.bindKeysAggregated('move', {
left: 'A',
right: 'D',
up: 'W',
down: 'S'
});
// Shooting
this.inputManager.bindKeys({
'Space': {
action: 'shoot',
mode: 'continuous' // Hold to shoot
}
});
// Or use profile
// this.inputManager.loadProfile('topdown');
}
update() {
this.inputManager.update();
}
} Twin-Stick Shooter
class GameScene extends Phaser.Scene {
create() {
const adapter = new PhaserAdapter(runtime, this);
this.inputManager = adapter.createInputManager();
// Movement (WASD)
this.inputManager.bindKeysAggregated('move', {
left: 'A',
right: 'D',
up: 'W',
down: 'S'
});
// Aim (Arrow keys)
this.inputManager.bindKeysAggregated('aim', {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'ArrowUp',
down: 'ArrowDown'
});
// Shoot
this.inputManager.bindKeys({
'Space': { action: 'shoot', mode: 'continuous' }
});
// Or use profile
// this.inputManager.loadProfile('twinstick');
}
update() {
this.inputManager.update();
}
} Fighting Game
class GameScene extends Phaser.Scene {
create() {
const adapter = new PhaserAdapter(runtime, this);
this.inputManager = adapter.createInputManager();
// Directional input
this.inputManager.bindKeysAggregated('direction', {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'ArrowUp',
down: 'ArrowDown'
});
// Attack buttons
this.inputManager.bindKeys({
'Z': { action: 'punch', mode: 'oneshot' },
'X': { action: 'kick', mode: 'oneshot' },
'C': { action: 'special', mode: 'oneshot' },
'Space': { action: 'block', mode: 'continuous' }
});
}
update() {
this.inputManager.update();
}
} Game Definition Integration
In your game definition:
import { defineGame, createInputAction } from '@martini-kit/core';
interface GameState {
players: Record<string, {
x: number;
y: number;
}>;
inputs: Record<string, {
left: boolean;
right: boolean;
up: boolean;
}>;
}
const game = defineGame<GameState>({
setup: ({ playerIds }) => ({
players: Object.fromEntries(
playerIds.map(id => [id, { x: 400, y: 300 }])
),
inputs: {}
}),
actions: {
// Store aggregated input
move: createInputAction('inputs'),
// One-shot action
jump: {
apply(state, context) {
const player = state.players[context.targetId];
if (player && player.onGround) {
player.vy = -400; // Jump velocity
}
}
}
}
}); Process input in update:
update() {
// InputManager submits actions automatically
this.inputManager.update();
// Read input from state
const state = runtime.getState();
const myInput = state.inputs[adapter.myId];
if (myInput) {
const speed = 200;
if (myInput.left) this.playerSprite.x -= speed * delta;
if (myInput.right) this.playerSprite.x += speed * delta;
}
} Best Practices
✅ Do
- Call
update()every frame - Inscene.update() - Use
bindKeysAggregated()for movement - Cleaner than individual keys - Use
mode: 'oneshot'for jumps/attacks - Prevents spam - Use input profiles - Quick setup for common patterns
- Store input in state - Use
createInputAction()
❌ Don’t
- Don’t forget to call
update()- Input won’t work - Don’t bind the same key twice - Later binding overwrites
- Don’t use continuous for one-time actions - Use oneshot instead
- Don’t read input directly in actions - Store in state first
See Also
- PhaserAdapter - Creating input managers
- Helpers (Core) -
createInputAction() - Actions - Action system