0
# Input Handling
1
2
Phaser provides comprehensive input handling for keyboard, mouse, touch, and gamepad input across desktop and mobile platforms. The input system supports both polling and event-driven approaches for maximum flexibility.
3
4
## Input Manager
5
6
### Scene Input Plugin
7
Each scene has its own input manager accessible via `this.input`:
8
9
```javascript { .api }
10
class InputScene extends Phaser.Scene {
11
create() {
12
const input = this.input;
13
14
// Input properties
15
console.log('Active pointer count:', input.activePointer);
16
console.log('Mouse enabled:', input.mouse.enabled);
17
console.log('Touch enabled:', input.touch.enabled);
18
console.log('Gamepad enabled:', input.gamepad.enabled);
19
20
// Global input events
21
input.on('pointerdown', this.handlePointerDown, this);
22
input.on('pointermove', this.handlePointerMove, this);
23
input.on('pointerup', this.handlePointerUp, this);
24
input.on('gameout', () => console.log('Pointer left game area'));
25
input.on('gameover', () => console.log('Pointer entered game area'));
26
}
27
28
handlePointerDown(pointer) {
29
console.log('Pointer down at:', pointer.x, pointer.y);
30
console.log('Left button:', pointer.leftButtonDown());
31
console.log('Right button:', pointer.rightButtonDown());
32
console.log('Middle button:', pointer.middleButtonDown());
33
}
34
}
35
```
36
37
### Pointer Objects
38
Pointers represent mouse cursors or touch points:
39
40
```javascript { .api }
41
class PointerScene extends Phaser.Scene {
42
create() {
43
this.input.on('pointerdown', (pointer) => {
44
// Pointer properties
45
console.log('Position:', pointer.x, pointer.y);
46
console.log('World position:', pointer.worldX, pointer.worldY);
47
console.log('Previous position:', pointer.prevPosition.x, pointer.prevPosition.y);
48
console.log('Velocity:', pointer.velocity.x, pointer.velocity.y);
49
console.log('Distance:', pointer.distance);
50
console.log('Duration:', pointer.getDuration());
51
console.log('Angle:', pointer.angle);
52
53
// Pointer identification
54
console.log('Pointer ID:', pointer.id);
55
console.log('Is primary pointer:', pointer.primaryDown);
56
console.log('Active buttons:', pointer.buttons);
57
58
// Touch-specific properties
59
console.log('Touch ID:', pointer.identifier);
60
console.log('Touch pressure:', pointer.pressure);
61
console.log('Touch size:', pointer.touchSizeX, pointer.touchSizeY);
62
});
63
64
// Multi-touch support
65
this.input.on('pointerdown', (pointer) => {
66
if (pointer.id === 0) {
67
console.log('First finger down');
68
} else if (pointer.id === 1) {
69
console.log('Second finger down - pinch gesture possible');
70
}
71
});
72
}
73
}
74
```
75
76
## Keyboard Input
77
78
### Cursor Keys
79
Quick access to arrow keys:
80
81
```javascript { .api }
82
class KeyboardScene extends Phaser.Scene {
83
create() {
84
// Create cursor keys object
85
this.cursors = this.input.keyboard.createCursorKeys();
86
}
87
88
update() {
89
// Polling approach
90
if (this.cursors.left.isDown) {
91
this.player.x -= 200 * this.game.loop.delta / 1000;
92
}
93
if (this.cursors.right.isDown) {
94
this.player.x += 200 * this.game.loop.delta / 1000;
95
}
96
if (this.cursors.up.isDown) {
97
this.player.y -= 200 * this.game.loop.delta / 1000;
98
}
99
if (this.cursors.down.isDown) {
100
this.player.y += 200 * this.game.loop.delta / 1000;
101
}
102
103
// Check for just pressed/released
104
if (Phaser.Input.Keyboard.JustDown(this.cursors.space)) {
105
this.playerJump();
106
}
107
if (Phaser.Input.Keyboard.JustUp(this.cursors.space)) {
108
this.playerLand();
109
}
110
}
111
}
112
```
113
114
### Individual Keys
115
Create and manage individual key objects:
116
117
```javascript { .api }
118
class IndividualKeysScene extends Phaser.Scene {
119
create() {
120
// Create individual keys
121
this.wasdKeys = this.input.keyboard.addKeys('W,S,A,D');
122
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
123
this.escKey = this.input.keyboard.addKey('ESC');
124
this.enterKey = this.input.keyboard.addKey(13); // Key code
125
126
// Key events
127
this.spaceKey.on('down', () => {
128
console.log('Space pressed');
129
});
130
131
this.spaceKey.on('up', () => {
132
console.log('Space released');
133
});
134
135
// Global keyboard events
136
this.input.keyboard.on('keydown', (event) => {
137
console.log('Key pressed:', event.code);
138
});
139
140
this.input.keyboard.on('keyup-ESC', () => {
141
this.scene.pause();
142
});
143
}
144
145
update() {
146
// WASD movement
147
const speed = 200;
148
const delta = this.game.loop.delta / 1000;
149
150
if (this.wasdKeys.A.isDown) {
151
this.player.x -= speed * delta;
152
}
153
if (this.wasdKeys.D.isDown) {
154
this.player.x += speed * delta;
155
}
156
if (this.wasdKeys.W.isDown) {
157
this.player.y -= speed * delta;
158
}
159
if (this.wasdKeys.S.isDown) {
160
this.player.y += speed * delta;
161
}
162
}
163
}
164
```
165
166
### Key Combinations
167
Handle complex key combinations:
168
169
```javascript { .api }
170
class KeyCombinationsScene extends Phaser.Scene {
171
create() {
172
// Create key combinations
173
this.keys = this.input.keyboard.addKeys({
174
'up': Phaser.Input.Keyboard.KeyCodes.W,
175
'down': Phaser.Input.Keyboard.KeyCodes.S,
176
'left': Phaser.Input.Keyboard.KeyCodes.A,
177
'right': Phaser.Input.Keyboard.KeyCodes.D,
178
'shift': Phaser.Input.Keyboard.KeyCodes.SHIFT,
179
'ctrl': Phaser.Input.Keyboard.KeyCodes.CTRL,
180
'alt': Phaser.Input.Keyboard.KeyCodes.ALT
181
});
182
183
// Key combo events
184
this.input.keyboard.createCombo([
185
Phaser.Input.Keyboard.KeyCodes.CTRL,
186
Phaser.Input.Keyboard.KeyCodes.S
187
], {
188
resetOnMatch: true,
189
maxKeyDelay: 0,
190
resetOnWrongKey: true,
191
deleteOnMatch: false
192
});
193
194
this.input.keyboard.on('keycombomatch', (combo) => {
195
console.log('Key combo matched!');
196
this.saveGame();
197
});
198
}
199
200
update() {
201
const speed = this.keys.shift.isDown ? 400 : 200; // Run when shift held
202
const delta = this.game.loop.delta / 1000;
203
204
// Modifier key combinations
205
if (this.keys.ctrl.isDown && this.keys.up.isDown) {
206
this.player.jump();
207
} else if (this.keys.up.isDown) {
208
this.player.y -= speed * delta;
209
}
210
211
if (this.keys.alt.isDown && this.keys.left.isDown) {
212
this.player.strafe(-1);
213
} else if (this.keys.left.isDown) {
214
this.player.x -= speed * delta;
215
}
216
}
217
}
218
```
219
220
### Key Codes
221
Common key code constants:
222
223
```javascript { .api }
224
const KeyCodes = Phaser.Input.Keyboard.KeyCodes;
225
226
// Letter keys
227
KeyCodes.A // 65
228
KeyCodes.B // 66
229
// ... through Z
230
231
// Number keys
232
KeyCodes.ZERO // 48
233
KeyCodes.ONE // 49
234
// ... through NINE (57)
235
236
// Function keys
237
KeyCodes.F1 // 112
238
KeyCodes.F2 // 113
239
// ... through F12 (123)
240
241
// Special keys
242
KeyCodes.SPACE // 32
243
KeyCodes.ENTER // 13
244
KeyCodes.ESC // 27
245
KeyCodes.TAB // 9
246
KeyCodes.SHIFT // 16
247
KeyCodes.CTRL // 17
248
KeyCodes.ALT // 18
249
250
// Arrow keys
251
KeyCodes.LEFT // 37
252
KeyCodes.UP // 38
253
KeyCodes.RIGHT // 39
254
KeyCodes.DOWN // 40
255
```
256
257
## Mouse and Touch Input
258
259
### Basic Mouse/Touch Events
260
Handle mouse and touch uniformly through pointers:
261
262
```javascript { .api }
263
class MouseTouchScene extends Phaser.Scene {
264
create() {
265
// Basic pointer events
266
this.input.on('pointerdown', (pointer, gameObject) => {
267
console.log('Pointer down at:', pointer.x, pointer.y);
268
this.createClickEffect(pointer.x, pointer.y);
269
});
270
271
this.input.on('pointermove', (pointer) => {
272
if (pointer.isDown) {
273
console.log('Dragging at:', pointer.x, pointer.y);
274
this.drawTrail(pointer.x, pointer.y);
275
}
276
});
277
278
this.input.on('pointerup', (pointer) => {
279
console.log('Pointer released after:', pointer.getDuration(), 'ms');
280
});
281
282
// Mouse-specific events
283
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
284
console.log('Mouse wheel:', deltaY);
285
this.cameras.main.zoom += deltaY > 0 ? -0.1 : 0.1;
286
});
287
288
// Right-click context menu
289
this.input.on('pointerdown', (pointer) => {
290
if (pointer.rightButtonDown()) {
291
this.showContextMenu(pointer.x, pointer.y);
292
}
293
});
294
}
295
296
createClickEffect(x, y) {
297
const effect = this.add.circle(x, y, 20, 0xffffff, 0.5);
298
this.tweens.add({
299
targets: effect,
300
scaleX: 2,
301
scaleY: 2,
302
alpha: 0,
303
duration: 300,
304
onComplete: () => effect.destroy()
305
});
306
}
307
}
308
```
309
310
### Interactive Objects
311
Make game objects respond to input:
312
313
```javascript { .api }
314
class InteractiveScene extends Phaser.Scene {
315
create() {
316
// Basic interactive object
317
const button = this.add.rectangle(400, 300, 200, 100, 0x00ff00);
318
button.setInteractive();
319
320
button.on('pointerdown', () => {
321
console.log('Button clicked!');
322
});
323
324
button.on('pointerover', () => {
325
button.setFillStyle(0x00aa00);
326
});
327
328
button.on('pointerout', () => {
329
button.setFillStyle(0x00ff00);
330
});
331
332
// Custom hit areas
333
const sprite = this.add.sprite(200, 200, 'player');
334
sprite.setInteractive(new Phaser.Geom.Circle(25, 25, 25), Phaser.Geom.Circle.Contains);
335
336
// Rectangle hit area
337
const image = this.add.image(600, 200, 'logo');
338
image.setInteractive(new Phaser.Geom.Rectangle(0, 0, 100, 50), Phaser.Geom.Rectangle.Contains);
339
340
// Pixel-perfect hit detection
341
const pixelSprite = this.add.sprite(400, 500, 'character');
342
pixelSprite.setInteractive({
343
pixelPerfect: true,
344
alphaTolerance: 1
345
});
346
}
347
}
348
```
349
350
### Drag and Drop
351
Implement drag and drop functionality:
352
353
```javascript { .api }
354
class DragDropScene extends Phaser.Scene {
355
create() {
356
// Create draggable objects
357
const box1 = this.add.rectangle(200, 200, 100, 100, 0xff0000);
358
const box2 = this.add.rectangle(400, 200, 100, 100, 0x00ff00);
359
const box3 = this.add.rectangle(600, 200, 100, 100, 0x0000ff);
360
361
// Make objects draggable
362
box1.setInteractive({ draggable: true });
363
box2.setInteractive({ draggable: true });
364
box3.setInteractive({ draggable: true });
365
366
// Drag events
367
this.input.on('dragstart', (pointer, gameObject) => {
368
console.log('Drag started:', gameObject);
369
gameObject.setTint(0x888888);
370
});
371
372
this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
373
gameObject.x = dragX;
374
gameObject.y = dragY;
375
});
376
377
this.input.on('dragend', (pointer, gameObject) => {
378
console.log('Drag ended:', gameObject);
379
gameObject.clearTint();
380
});
381
382
// Drop zones
383
const dropZone = this.add.zone(400, 500, 200, 100);
384
dropZone.setRectangleDropZone(200, 100);
385
386
// Visual feedback for drop zone
387
const dropZoneGraphic = this.add.graphics();
388
dropZoneGraphic.lineStyle(2, 0xffffff);
389
dropZoneGraphic.strokeRect(300, 450, 200, 100);
390
391
// Drop events
392
this.input.on('drop', (pointer, gameObject, dropZone) => {
393
console.log('Object dropped in zone!');
394
gameObject.x = dropZone.x;
395
gameObject.y = dropZone.y;
396
});
397
398
this.input.on('dragenter', (pointer, gameObject, dropZone) => {
399
dropZoneGraphic.clear();
400
dropZoneGraphic.lineStyle(2, 0x00ff00);
401
dropZoneGraphic.strokeRect(300, 450, 200, 100);
402
});
403
404
this.input.on('dragleave', (pointer, gameObject, dropZone) => {
405
dropZoneGraphic.clear();
406
dropZoneGraphic.lineStyle(2, 0xffffff);
407
dropZoneGraphic.strokeRect(300, 450, 200, 100);
408
});
409
}
410
}
411
```
412
413
## Gamepad Input
414
415
### Gamepad Setup
416
Support for multiple gamepads:
417
418
```javascript { .api }
419
class GamepadScene extends Phaser.Scene {
420
create() {
421
// Enable gamepad input
422
this.input.gamepad.start();
423
424
// Listen for gamepad connection
425
this.input.gamepad.on('connected', (pad) => {
426
console.log('Gamepad connected:', pad.index, pad.id);
427
this.setupGamepad(pad);
428
});
429
430
this.input.gamepad.on('disconnected', (pad) => {
431
console.log('Gamepad disconnected:', pad.index);
432
});
433
434
// Get existing gamepads
435
if (this.input.gamepad.total > 0) {
436
this.gamepad = this.input.gamepad.pad1; // First gamepad
437
this.setupGamepad(this.gamepad);
438
}
439
}
440
441
setupGamepad(pad) {
442
// Button events
443
pad.on('down', (index, value, button) => {
444
console.log('Button pressed:', index, button.id);
445
});
446
447
pad.on('up', (index, value, button) => {
448
console.log('Button released:', index);
449
});
450
451
// Specific button events
452
if (pad.A) {
453
pad.A.on('down', () => {
454
this.playerJump();
455
});
456
}
457
458
if (pad.B) {
459
pad.B.on('down', () => {
460
this.playerAttack();
461
});
462
}
463
}
464
465
update() {
466
if (this.gamepad) {
467
// Left stick movement
468
const leftStick = this.gamepad.leftStick;
469
if (leftStick.length > 0.1) { // Deadzone
470
this.player.x += leftStick.x * 200 * (this.game.loop.delta / 1000);
471
this.player.y += leftStick.y * 200 * (this.game.loop.delta / 1000);
472
}
473
474
// Right stick camera
475
const rightStick = this.gamepad.rightStick;
476
if (rightStick.length > 0.1) {
477
this.cameras.main.scrollX += rightStick.x * 100 * (this.game.loop.delta / 1000);
478
this.cameras.main.scrollY += rightStick.y * 100 * (this.game.loop.delta / 1000);
479
}
480
481
// D-pad
482
if (this.gamepad.left) {
483
this.selectMenuItem(-1);
484
}
485
if (this.gamepad.right) {
486
this.selectMenuItem(1);
487
}
488
489
// Triggers
490
if (this.gamepad.L2 > 0.5) {
491
this.aimWeapon();
492
}
493
if (this.gamepad.R2 > 0.5) {
494
this.fireWeapon(this.gamepad.R2); // Pressure sensitive
495
}
496
}
497
}
498
}
499
```
500
501
### Gamepad Button Mapping
502
Access gamepad buttons by name or index:
503
504
```javascript { .api }
505
class GamepadMappingScene extends Phaser.Scene {
506
update() {
507
const pad = this.input.gamepad.pad1;
508
509
if (pad) {
510
// Face buttons (Xbox layout)
511
if (pad.A && pad.A.pressed) { console.log('A pressed'); }
512
if (pad.B && pad.B.pressed) { console.log('B pressed'); }
513
if (pad.X && pad.X.pressed) { console.log('X pressed'); }
514
if (pad.Y && pad.Y.pressed) { console.log('Y pressed'); }
515
516
// Shoulder buttons
517
if (pad.L1 && pad.L1.pressed) { console.log('Left bumper'); }
518
if (pad.R1 && pad.R1.pressed) { console.log('Right bumper'); }
519
520
// Triggers (analog values)
521
if (pad.L2 > 0) { console.log('Left trigger:', pad.L2); }
522
if (pad.R2 > 0) { console.log('Right trigger:', pad.R2); }
523
524
// D-pad
525
if (pad.up) { console.log('D-pad up'); }
526
if (pad.down) { console.log('D-pad down'); }
527
if (pad.left) { console.log('D-pad left'); }
528
if (pad.right) { console.log('D-pad right'); }
529
530
// Stick buttons
531
if (pad.L3 && pad.L3.pressed) { console.log('Left stick pressed'); }
532
if (pad.R3 && pad.R3.pressed) { console.log('Right stick pressed'); }
533
534
// System buttons
535
if (pad.select && pad.select.pressed) { console.log('Select/Back'); }
536
if (pad.start && pad.start.pressed) { console.log('Start/Menu'); }
537
}
538
}
539
}
540
```
541
542
## Advanced Input Techniques
543
544
### Input Sequences
545
Detect complex input patterns:
546
547
```javascript { .api }
548
class InputSequenceScene extends Phaser.Scene {
549
create() {
550
// Fighting game combo system
551
this.comboSequence = [];
552
this.comboTimer = 0;
553
this.maxComboDelay = 1000; // 1 second between inputs
554
555
// Define combos
556
this.combos = {
557
'hadoken': ['down', 'down-forward', 'forward', 'punch'],
558
'shoryuken': ['forward', 'down', 'down-forward', 'punch'],
559
'spin-kick': ['back', 'back', 'forward', 'kick']
560
};
561
562
// Input detection
563
this.input.keyboard.on('keydown-S', () => this.addToCombo('down'));
564
this.input.keyboard.on('keydown-W', () => this.addToCombo('up'));
565
this.input.keyboard.on('keydown-A', () => this.addToCombo('back'));
566
this.input.keyboard.on('keydown-D', () => this.addToCombo('forward'));
567
this.input.keyboard.on('keydown-J', () => this.addToCombo('punch'));
568
this.input.keyboard.on('keydown-K', () => this.addToCombo('kick'));
569
}
570
571
addToCombo(input) {
572
this.comboSequence.push(input);
573
this.comboTimer = this.time.now;
574
575
// Check for combo matches
576
this.checkCombos();
577
578
// Clear old inputs
579
if (this.comboSequence.length > 10) {
580
this.comboSequence.shift();
581
}
582
}
583
584
checkCombos() {
585
const sequence = this.comboSequence.join('-');
586
587
for (let [comboName, comboInputs] of Object.entries(this.combos)) {
588
const comboString = comboInputs.join('-');
589
if (sequence.includes(comboString)) {
590
this.executeCombo(comboName);
591
this.comboSequence = [];
592
break;
593
}
594
}
595
}
596
597
update(time) {
598
// Clear combo if too much time has passed
599
if (time - this.comboTimer > this.maxComboDelay) {
600
this.comboSequence = [];
601
}
602
}
603
}
604
```
605
606
### Gesture Recognition
607
Basic gesture detection for touch input:
608
609
```javascript { .api }
610
class GestureScene extends Phaser.Scene {
611
create() {
612
this.gesturePoints = [];
613
this.isRecording = false;
614
615
this.input.on('pointerdown', (pointer) => {
616
this.isRecording = true;
617
this.gesturePoints = [{ x: pointer.x, y: pointer.y, time: this.time.now }];
618
});
619
620
this.input.on('pointermove', (pointer) => {
621
if (this.isRecording) {
622
this.gesturePoints.push({
623
x: pointer.x,
624
y: pointer.y,
625
time: this.time.now
626
});
627
}
628
});
629
630
this.input.on('pointerup', () => {
631
this.isRecording = false;
632
this.recognizeGesture();
633
});
634
}
635
636
recognizeGesture() {
637
if (this.gesturePoints.length < 3) return;
638
639
const startPoint = this.gesturePoints[0];
640
const endPoint = this.gesturePoints[this.gesturePoints.length - 1];
641
642
const deltaX = endPoint.x - startPoint.x;
643
const deltaY = endPoint.y - startPoint.y;
644
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
645
646
if (distance > 100) { // Minimum swipe distance
647
const angle = Math.atan2(deltaY, deltaX);
648
const degrees = Phaser.Math.RadToDeg(angle);
649
650
if (degrees > -45 && degrees <= 45) {
651
this.onSwipeRight();
652
} else if (degrees > 45 && degrees <= 135) {
653
this.onSwipeDown();
654
} else if (degrees > 135 || degrees <= -135) {
655
this.onSwipeLeft();
656
} else {
657
this.onSwipeUp();
658
}
659
} else if (this.gesturePoints.length < 10) {
660
this.onTap();
661
}
662
}
663
664
onSwipeLeft() { console.log('Swiped left'); }
665
onSwipeRight() { console.log('Swiped right'); }
666
onSwipeUp() { console.log('Swiped up'); }
667
onSwipeDown() { console.log('Swiped down'); }
668
onTap() { console.log('Tapped'); }
669
}
670
```
671
672
### Multi-Touch Gestures
673
Handle complex multi-touch interactions:
674
675
```javascript { .api }
676
class MultiTouchScene extends Phaser.Scene {
677
create() {
678
this.pinchStart = null;
679
this.initialDistance = 0;
680
681
this.input.on('pointerdown', (pointer) => {
682
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
683
// Two fingers down - start pinch gesture
684
this.startPinch();
685
}
686
});
687
688
this.input.on('pointermove', () => {
689
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
690
this.updatePinch();
691
}
692
});
693
694
this.input.on('pointerup', () => {
695
if (!this.input.pointer1.isDown || !this.input.pointer2.isDown) {
696
this.endPinch();
697
}
698
});
699
}
700
701
startPinch() {
702
const pointer1 = this.input.pointer1;
703
const pointer2 = this.input.pointer2;
704
705
this.initialDistance = Phaser.Math.Distance.Between(
706
pointer1.x, pointer1.y,
707
pointer2.x, pointer2.y
708
);
709
710
this.pinchStart = {
711
zoom: this.cameras.main.zoom,
712
centerX: (pointer1.x + pointer2.x) / 2,
713
centerY: (pointer1.y + pointer2.y) / 2
714
};
715
}
716
717
updatePinch() {
718
if (!this.pinchStart) return;
719
720
const pointer1 = this.input.pointer1;
721
const pointer2 = this.input.pointer2;
722
723
const currentDistance = Phaser.Math.Distance.Between(
724
pointer1.x, pointer1.y,
725
pointer2.x, pointer2.y
726
);
727
728
const scale = currentDistance / this.initialDistance;
729
const newZoom = this.pinchStart.zoom * scale;
730
731
this.cameras.main.setZoom(Phaser.Math.Clamp(newZoom, 0.5, 3));
732
}
733
734
endPinch() {
735
this.pinchStart = null;
736
}
737
}
738
```
739
740
This comprehensive input system provides all the tools needed to create responsive, multi-platform games that work seamlessly across desktop and mobile devices.