关于 phaser-input
phaser-input 是介绍 Phaser 4 输入系统的技能,涵盖键盘、指针、触控和游戏手柄操作。它提供常见 API 用法,并附有移动端虚拟摇杆参考实现。
主要功能
- 01键盘绑定与按键状态
- 02指针事件和拖放
- 03触控与多点触控
- 04手柄按键与模拟摇杆
- 05虚拟摇杆参考实现
适用场景
- 01为游戏玩法映射键盘操作
- 02添加可点击和可拖动对象
- 03处理多点触控交互
- 04构建移动端移动控制
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yakoub-ai/ phaser4-gamedev --skill phaser-input安装后在 Claude Code 中输入/phaser-input确认已加载。
附带文件references/input-api.mdreferences/virtual-joystick.md
详细介绍
Phaser 4 输入
Phaser 4 的输入系统支持键盘、鼠标/指针、触摸和游戏手柄。在场景中,所有输入都通过 this.input 访问。
键盘输入
方向键
一次调用即可创建方向键集合,用于跟踪方向键、空格键和 Shift 键:
private cursors!: Phaser.Types.Input.Keyboard.CursorKeys;
create(): void {
this.cursors = this.input.keyboard!.createCursorKeys();
}
update(): void {
if (this.cursors.left.isDown) { /* move left */ }
if (this.cursors.right.isDown) { /* move right */ }
if (this.cursors.up.isDown) { /* move up / jump */ }
if (this.cursors.down.isDown) { /* move down / crouch */ }
if (this.cursors.space.isDown) { /* fire / action */ }
if (this.cursors.shift.isDown) { /* sprint / modifier */ }
}
WASD 键
使用 addKeys 按名称绑定任意按键:
private wasd!: { up: Phaser.Input.Keyboard.Key; left: Phaser.Input.Keyboard.Key; down: Phaser.Input.Keyboard.Key; right: Phaser.Input.Keyboard.Key };
create(): void {
this.wasd = this.input.keyboard!.addKeys({ up: 'W', left: 'A', down: 'S', right: 'D' }) as typeof this.wasd;
}
update(): void {
if (this.wasd.left.isDown) { /* strafe left */ }
if (this.wasd.right.isDown) { /* strafe right */ }
}
单个按键
使用 KeyCodes 绑定单个按键:
private spaceKey!: Phaser.Input.Keyboard.Key;
private shiftKey!: Phaser.Input.Keyboard.Key;
create(): void {
this.spaceKey = this.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.shiftKey = this.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT);
}
isDown、JustDown 与 JustUp
| 方法 | 行为 |
|---|---|
key.isDown | 按住按键时,每帧都为 true |
Phaser.Input.Keyboard.JustDown(key) | 按键首次按下时,仅在那一帧为 true |
Phaser.Input.Keyboard.JustUp(key) | 按键释放时,仅在那一帧为 true |
update(): void {
// Continuous — fires every frame while held
if (this.cursors.left.isDown) {
this.player.setVelocityX(-200);
}
// One-shot — fires once per key press
if (Phaser.Input.Keyboard.JustDown(this.spaceKey)) {
this.fireBullet();
}
// Release — fires once per key release
if (Phaser.Input.Keyboard.JustUp(this.shiftKey)) {
this.stopSprint();
}
}
按键组合(Konami 密码、作弊码)
create(): void {
// Konami: Up Up Down Down Left Right Left Right A B
const combo = this.input.keyboard!.createCombo(
[38, 38, 40, 40, 37, 39, 37, 39, 65, 66],
{ resetOnMatch: true }
);
this.input.keyboard!.on('keycombomatch', (combo: Phaser.Input.Keyboard.KeyCombo) => {
console.log('Konami code entered!');
this.activateCheatMode();
});
}
禁用浏览器默认按键行为
防止浏览器拦截方向键(滚动页面)和其他按键:
create(): void {
// Disable global capture — let browser handle keys normally
this.input.keyboard!.disableGlobalCapture();
// Enable global capture — Phaser intercepts keys before the browser
this.input.keyboard!.enableGlobalCapture();
// Capture only specific keys (recommended approach)
this.input.keyboard!.addCapture([
Phaser.Input.Keyboard.KeyCodes.UP,
Phaser.Input.Keyboard.KeyCodes.DOWN,
Phaser.Input.Keyboard.KeyCodes.LEFT,
Phaser.Input.Keyboard.KeyCodes.RIGHT,
Phaser.Input.Keyboard.KeyCodes.SPACE,
]);
}
指针/鼠标输入
全局指针事件
在 this.input 上监听游戏画布任意位置的事件:
create(): void {
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
console.log(`Clicked at canvas: ${ptr.x}, ${ptr.y}`);
console.log(`World position: ${ptr.worldX}, ${ptr.worldY}`);
console.log(`Left button: ${ptr.leftButtonDown()}`);
console.log(`Right button: ${ptr.rightButtonDown()}`);
});
this.input.on('pointermove', (ptr: Phaser.Input.Pointer) => {
// ptr.velocity.x / ptr.velocity.y for movement delta
});
this.input.on('pointerup', (ptr: Phaser.Input.Pointer) => {
// handle release
});
}
摄像机移动后,请使用 ptr.worldX/ptr.worldY,它们提供的是世界坐标,而非屏幕坐标。
让精灵可交互
在监听特定对象的指针事件之前,先调用 setInteractive():
create(): void {
const button = this.add.image(400, 300, 'button');
button.setInteractive({ useHandCursor: true }); // shows pointer cursor on hover
button.on('pointerdown', () => {
this.scene.start('GameScene');
});
button.on('pointerover', () => {
button.setTint(0xcccccc); // lighten on hover
});
button.on('pointerout', () => {
button.clearTint();
});
}
拖放
启用游戏对象的拖动功能:
create(): void {
const card = this.add.image(200, 200, 'card');
card.setInteractive();
this.input.setDraggable(card);
this.input.on('dragstart', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image) => {
obj.setTint(0x88ff88);
});
this.input.on('drag', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image, dragX: number, dragY: number) => {
obj.setPosition(dragX, dragY);
});
this.input.on('dragend', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image) => {
obj.clearTint();
});
}
输入区域
使用 Zone 作为不可见的交互区域(适用于 UI 点击区域):
create(): void {
const zone = this.add.zone(400, 300, 200, 100).setInteractive();
zone.on('pointerdown', () => {
console.log('Zone was clicked');
});
}
右键(上下文菜单)
防止右键时显示浏览器上下文菜单:
create(): void {
this.input.mouse!.disableContextMenu();
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
if (ptr.rightButtonDown()) {
console.log('Right click at', ptr.worldX, ptr.worldY);
}
});
}
阻止事件传播
防止点击游戏对象时同时触发全局 pointerdown 处理程序:
button.on('pointerdown', (
ptr: Phaser.Input.Pointer,
localX: number,
localY: number,
event: Phaser.Types.Input.EventData
) => {
event.stopPropagation();
this.handleButtonClick();
});
触摸输入
触摸事件与指针 API 共用相同接口——在触摸设备上会自动触发 pointerdown、pointermove 和 pointerup。
多点触控
添加额外的指针槽位(默认总共 2 个;需要支持更多手指时可扩展):
create(): void {
// Support up to 4 simultaneous touches
this.input.addPointer(2); // adds 2 more (total becomes 4)
// Access individual touch pointers
// this.input.pointer1 — first touch
// this.input.pointer2 — second touch
// this.input.pointer3 — third touch
}
滑动检测
Phaser 没有内置的滑动 API。请手动跟踪起始位置:
private swipeStart = { x: 0, y: 0 };
private readonly SWIPE_THRESHOLD = 50; // pixels
create(): void {
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
this.swipeStart = { x: ptr.x, y: ptr.y };
});
this.input.on('pointerup', (ptr: Phaser.Input.Pointer) => {
const dx = ptr.x - this.swipeStart.x;
const dy = ptr.y - this.swipeStart.y;
if (Math.abs(dx) > this.SWIPE_THRESHOLD || Math.abs(dy) > this.SWIPE_THRESHOLD) {
if (Math.abs(dx) > Math.abs(dy)) {
console.log(dx > 0 ? 'Swipe Right' : 'Swipe Left');
} else {
console.log(dy > 0 ? 'Swipe Down' : 'Swipe Up');
}
}
});
}
阻止浏览器手势
create(): void {
// Prevent pinch-zoom, browser swipe navigation, pull-to-refresh
this.input.mouse!.disableContextMenu();
// In your HTML, also add to the canvas element:
// style="touch-action: none;"
// Or in Phaser config: input: { activePointers: 3 }
}
游戏手柄输入
Phaser 4 默认包含 Gamepad 插件。通过 this.input.gamepad 访问它。
连接
create(): void {
this.input.gamepad!.on('connected', (pad: Phaser.Input.Gamepad.Gamepad) => {
console.log(`Gamepad connected: ${pad.id}`);
});
this.input.gamepad!.on('disconnected', (pad: Phaser.Input.Gamepad.Gamepad) => {
console.log(`Gamepad disconnected`);
});
}
读取按键
update(): void {
const pad = this.input.gamepad!.pad1;
if (!pad) return;
// Face buttons
if (pad.A) { this.jump(); }
if (pad.B) { this.dodge(); }
if (pad.X) { this.attack(); }
if (pad.Y) { this.interact(); }
// Shoulder / trigger buttons
if (pad.L1) { /* left bumper */ }
if (pad.R1) { /* right bumper */ }
if (pad.L2) { /* left trigger (analog, 0–1) */ }
if (pad.R2) { /* right trigger (analog, 0–1) */ }
// D-pad
if (pad.up) { /* d-pad up */ }
if (pad.down) { /* d-pad down */ }
if (pad.left) { /* d-pad left */ }
if (pad.right) { /* d-pad right */ }
}
摇杆
每个轴的取值范围为 -1 到 1。请设置死区以防止漂移:
private readonly DEAD_ZONE = 0.1;
update(): void {
const pad = this.input.gamepad!.pad1;
if (!pad) return;
let lx = pad.leftStick.x;
let ly = pad.leftStick.y;
let rx = pad.rightStick.x;
let ry = pad.rightStick.y;
// Apply dead zone
if (Math.abs(lx) < this.DEAD_ZONE) lx = 0;
if (Math.abs(ly) < this.DEAD_ZONE) ly = 0;
if (Math.abs(rx) < this.DEAD_ZONE) rx = 0;
if (Math.abs(ry) < this.DEAD_ZONE) ry = 0;
this.player.setVelocity(lx * 300, ly * 300);
// Aim with right stick
if (rx !== 0 || ry !== 0) {
const angle = Math.atan2(ry, rx) * (180 / Math.PI);
this.player.setAngle(angle);
}
}
虚拟摇杆(移动设备)
Phaser 4 没有内置虚拟摇杆。对于移动游戏,请实现基于指针的摇杆,或使用参考实现。
请参阅 references/virtual-joystick.md,其中包含完整且可用于生产环境的 TypeScript 类,可绘制底座圆和摇杆帽,并提供归一化的 direction.x/direction.y 值。
该参考文件记录了两个常见错误:(1)首次触点锁定——拖动期间底座必须保持锁定;如果用户拖动时底座会“跳动”,说明有人在 onPointerMove 中更新了 baseX/baseY。(2)跨场景监听器丢失——如果摇杆在一个场景中有效、在另一个场景中无效,请通过 scene.launch() 在持久化的 InputScene 中仅实例化一次,而不要在每个游戏场景中分别实例化。
使用方式:
private joystick!: VirtualJoystick;
create(): void {
this.joystick = new VirtualJoystick(this, 120, this.scale.height - 120);
}
update(): void {
if (this.joystick.isActive) {
this.player.setVelocity(
this.joystick.direction.x * 200,
this.joystick.direction.y * 200
);
} else {
this.player.setVelocity(0, 0);
}
}
更多资源
参考文件
references/input-api.md—— InputPlugin、KeyboardPlugin、Key、Pointer、Gamepad 及所有输入事件的完整 API 参考references/virtual-joystick.md—— 适用于移动游戏的完整 TypeScript 虚拟摇杆实现