miaoda-game-controls-phaser
v0.3.3
Published
Unified Phaser keyboard, gamepad, touch, gesture, buffered action, and input-context controls.
Maintainers
Readme
miaoda-game-controls-phaser
One named input model for keyboard, gamepad, touch controls, virtual sticks, and pointer gestures in Phaser 4. Choose it when gameplay should read the same actions regardless of the device.
pnpm add miaoda-game-controls-phaserimport {Controls, STANDARD_GAMEPAD} from 'miaoda-game-controls-phaser';
const controls = new Controls(this);
controls.addKeyboard(this.input.keyboard!, {axes: {move: {left: ['A', 'LEFT'], right: ['D', 'RIGHT']}}, buttons: {jump: 'SPACE'}});
controls.addGamepad(this.input.gamepad!, {axes: {move: STANDARD_GAMEPAD.axes.move}, buttons: {jump: STANDARD_GAMEPAD.buttons.south}});
const move = controls.axis('move');
if (controls.pressed('jump')) player.jump();Read axis, down, pressed, released, or consumeBuffered. Bindings with the same name merge across devices. consumeBuffered(name, milliseconds) is useful for forgiving jump/input timing.
Touch, gestures, and contexts
Use addVirtualStick, addVirtualButton, and addGestures to map touch input to the same names. Gesture names include tap, doubleTap, longPress, swipe, swipeUp, swipeDown, swipeLeft, and swipeRight.
Bindings can use gameplay, menu, or another context. Call setContext(name) to switch scoped bindings; unscoped bindings remain active.
Controls samples during Phaser preupdate. For a strict fixed-step loop, sample
devices once per Phaser host frame and write the result to the engine-neutral
LogicalInputBuffer; do not poll the device again for every catch-up tick:
import { FixedStepper, LogicalInputBuffer } from 'miaoda-game-fixed-step-core';
const controls = new Controls(scene, { autoUpdate: false });
const input = new LogicalInputBuffer();
const fixedStepper = new FixedStepper({ tickRate: 60 });
function update(_time: number, deltaMs: number) {
controls.update();
const move = controls.axis('move');
input.sampleHostFrame({
buttons: { jump: controls.down('jump') },
axes: { moveX: move.x, moveY: move.y },
});
fixedStepper.advance(deltaMs / 1000, ({ dt }) => {
game.step(dt, input.nextTick());
});
}Controls remains the Phaser device adapter; it does not become a simulation
clock. Call destroy() when the instance leaves its Scene; it removes listeners
and generated touch visuals and is terminal. Reads then return neutral values,
sampling callbacks stop immediately, and attempts to add new bindings or device
sources throw. Owned GestureSource instances also reject already-captured
pointer and long-press timer callbacks after teardown.
Standalone VirtualStick and VirtualButton instances follow the same terminal, idempotent teardown rule; caller-supplied visuals remain caller-owned.
