0
# Camera System
1
2
Phaser's camera system provides flexible viewport management, smooth following, visual effects, and multi-camera support. Cameras determine what portion of the game world is visible and how it's rendered to the screen.
3
4
## Camera Manager
5
6
### Basic Camera Setup
7
Each scene has a camera manager that controls one or more cameras:
8
9
```javascript { .api }
10
class CameraScene extends Phaser.Scene {
11
create() {
12
// Access the main camera
13
const mainCamera = this.cameras.main;
14
15
// Camera properties
16
console.log('Camera position:', mainCamera.scrollX, mainCamera.scrollY);
17
console.log('Camera size:', mainCamera.width, mainCamera.height);
18
console.log('Camera zoom:', mainCamera.zoom);
19
console.log('Camera bounds:', mainCamera.getBounds());
20
21
// Basic camera operations
22
mainCamera.setPosition(100, 50); // Set camera viewport position
23
mainCamera.setSize(600, 400); // Set camera viewport size
24
mainCamera.setScroll(200, 100); // Set world scroll position
25
mainCamera.setZoom(1.5); // Set zoom level
26
mainCamera.setRotation(0.1); // Rotate camera view
27
28
// Camera background
29
mainCamera.setBackgroundColor('#2c3e50');
30
mainCamera.setAlpha(0.8); // Camera transparency
31
mainCamera.setVisible(true); // Camera visibility
32
}
33
}
34
```
35
36
### Multiple Cameras
37
Create and manage multiple cameras for split-screen or UI overlays:
38
39
```javascript { .api }
40
class MultiCameraScene extends Phaser.Scene {
41
create() {
42
// Main camera covers full screen
43
const mainCamera = this.cameras.main;
44
mainCamera.setViewport(0, 0, 800, 600);
45
46
// Add secondary camera for minimap
47
const minimap = this.cameras.add(600, 20, 180, 140);
48
minimap.setZoom(0.2);
49
minimap.setName('minimap');
50
minimap.setBackgroundColor('#000000');
51
52
// Add UI camera that ignores game objects
53
const uiCamera = this.cameras.add(0, 0, 800, 600);
54
uiCamera.setName('ui');
55
56
// Make UI elements only visible to UI camera
57
const healthBar = this.add.rectangle(50, 50, 100, 20, 0xff0000);
58
healthBar.setScrollFactor(0, 0); // Don't scroll with main camera
59
60
// Ignore specific objects on specific cameras
61
mainCamera.ignore(healthBar); // Main camera doesn't render UI
62
uiCamera.ignore([this.player, this.enemies]); // UI camera only renders UI
63
64
// Camera management
65
const cameras = this.cameras.getCamera('minimap');
66
this.cameras.remove(minimap); // Remove camera
67
this.cameras.removeAll(); // Remove all cameras except main
68
69
// Camera iteration
70
this.cameras.cameras.forEach(camera => {
71
console.log('Camera:', camera.name);
72
});
73
}
74
}
75
```
76
77
## Camera Movement
78
79
### Manual Control
80
Directly control camera position and properties:
81
82
```javascript { .api }
83
class CameraControlScene extends Phaser.Scene {
84
create() {
85
this.cameras.main.setScroll(0, 0);
86
87
// Keyboard controls
88
this.cursors = this.input.keyboard.createCursorKeys();
89
this.wasd = this.input.keyboard.addKeys('W,S,A,D');
90
}
91
92
update() {
93
const camera = this.cameras.main;
94
const speed = 5;
95
96
// WASD camera movement
97
if (this.wasd.A.isDown) {
98
camera.scrollX -= speed;
99
} else if (this.wasd.D.isDown) {
100
camera.scrollX += speed;
101
}
102
103
if (this.wasd.W.isDown) {
104
camera.scrollY -= speed;
105
} else if (this.wasd.S.isDown) {
106
camera.scrollY += speed;
107
}
108
109
// Mouse wheel zoom
110
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => {
111
if (deltaY > 0) {
112
camera.zoom = Phaser.Math.Clamp(camera.zoom - 0.1, 0.1, 3);
113
} else {
114
camera.zoom = Phaser.Math.Clamp(camera.zoom + 0.1, 0.1, 3);
115
}
116
});
117
118
// Smooth camera movement with lerp
119
const targetScrollX = this.player.x - 400;
120
const targetScrollY = this.player.y - 300;
121
122
camera.scrollX = Phaser.Math.Linear(camera.scrollX, targetScrollX, 0.05);
123
camera.scrollY = Phaser.Math.Linear(camera.scrollY, targetScrollY, 0.05);
124
}
125
}
126
```
127
128
### Camera Following
129
Make the camera automatically follow game objects:
130
131
```javascript { .api }
132
class CameraFollowScene extends Phaser.Scene {
133
create() {
134
this.player = this.add.sprite(400, 300, 'player');
135
136
// Basic following
137
this.cameras.main.startFollow(this.player);
138
139
// Following with options
140
this.cameras.main.startFollow(
141
this.player, // Target to follow
142
false, // Round pixels
143
0.05, // Lerp X (0 = instant, 1 = never catch up)
144
0.05, // Lerp Y
145
0, // Offset X
146
0 // Offset Y
147
);
148
149
// Set follow offset
150
this.cameras.main.setFollowOffset(-100, -50);
151
152
// Deadzone following (only move when target leaves zone)
153
this.cameras.main.setDeadzone(200, 150);
154
155
// Linear following with specific lerp values
156
this.cameras.main.setLerp(0.1, 0.1);
157
158
// Stop following
159
this.cameras.main.stopFollow();
160
161
// Conditional following
162
this.followActive = true;
163
if (this.followActive) {
164
this.cameras.main.startFollow(this.player, true, 0.08, 0.08);
165
}
166
}
167
168
update() {
169
// Dynamic follow target switching
170
if (this.input.keyboard.addKey('1').isDown) {
171
this.cameras.main.startFollow(this.player);
172
} else if (this.input.keyboard.addKey('2').isDown) {
173
this.cameras.main.startFollow(this.enemy);
174
}
175
176
// Adjust follow based on player state
177
if (this.player.body.velocity.x > 100) {
178
// Look ahead when moving fast
179
this.cameras.main.setFollowOffset(50, 0);
180
} else {
181
this.cameras.main.setFollowOffset(0, 0);
182
}
183
}
184
}
185
```
186
187
### Camera Bounds
188
Constrain camera movement within world boundaries:
189
190
```javascript { .api }
191
class CameraBoundsScene extends Phaser.Scene {
192
create() {
193
// Set world bounds
194
this.physics.world.setBounds(0, 0, 2000, 1200);
195
196
// Set camera bounds (camera won't scroll outside these)
197
this.cameras.main.setBounds(0, 0, 2000, 1200);
198
199
// Camera bounds with centering
200
this.cameras.main.setBounds(0, 0, 2000, 1200, true);
201
202
// Get current bounds
203
const bounds = this.cameras.main.getBounds();
204
console.log('Camera bounds:', bounds);
205
206
// Remove bounds
207
this.cameras.main.removeBounds();
208
209
// Dynamic bounds adjustment
210
this.events.on('levelComplete', () => {
211
// Expand camera bounds for next level
212
this.cameras.main.setBounds(0, 0, 3000, 1500);
213
});
214
}
215
}
216
```
217
218
## Camera Effects
219
220
### Fade Effects
221
Smooth fade in/out transitions:
222
223
```javascript { .api }
224
class CameraFadeScene extends Phaser.Scene {
225
create() {
226
// Fade in from black
227
this.cameras.main.fadeIn(1000); // Duration in ms
228
229
// Fade out to black
230
this.cameras.main.fadeOut(1000);
231
232
// Fade to specific color
233
this.cameras.main.fadeOut(1000, 255, 0, 0); // Fade to red
234
235
// Fade with callback
236
this.cameras.main.fadeOut(1000, 0, 0, 0, (camera, progress) => {
237
if (progress === 1) {
238
console.log('Fade complete');
239
this.scene.start('NextScene');
240
}
241
});
242
243
// Fade events
244
this.cameras.main.on('camerafadeincomplete', () => {
245
console.log('Fade in complete');
246
});
247
248
this.cameras.main.on('camerafadeoutcomplete', () => {
249
console.log('Fade out complete');
250
});
251
252
// Check fade status
253
console.log('Is fading:', this.cameras.main.fadeEffect.isRunning);
254
console.log('Fade progress:', this.cameras.main.fadeEffect.progress);
255
}
256
257
fadeToScene(sceneKey) {
258
this.cameras.main.fadeOut(1000);
259
this.cameras.main.once('camerafadeoutcomplete', () => {
260
this.scene.start(sceneKey);
261
});
262
}
263
}
264
```
265
266
### Flash Effects
267
Screen flash for impact or attention:
268
269
```javascript { .api }
270
class CameraFlashScene extends Phaser.Scene {
271
create() {
272
this.player = this.add.sprite(400, 300, 'player');
273
274
// White flash
275
this.cameras.main.flash(250); // Duration in ms
276
277
// Colored flash
278
this.cameras.main.flash(500, 255, 0, 0); // Red flash
279
280
// Flash with callback
281
this.cameras.main.flash(300, 255, 255, 0, false, (camera, progress) => {
282
if (progress === 1) {
283
console.log('Flash complete');
284
}
285
});
286
287
// Flash events
288
this.cameras.main.on('cameraflashstart', () => {
289
console.log('Flash started');
290
});
291
292
this.cameras.main.on('cameraflashcomplete', () => {
293
console.log('Flash complete');
294
});
295
}
296
297
playerHit() {
298
// Flash red when player takes damage
299
this.cameras.main.flash(200, 255, 0, 0);
300
}
301
302
powerUpCollected() {
303
// Flash yellow when power-up collected
304
this.cameras.main.flash(300, 255, 255, 0);
305
}
306
}
307
```
308
309
### Shake Effects
310
Screen shake for explosions and impacts:
311
312
```javascript { .api }
313
class CameraShakeScene extends Phaser.Scene {
314
create() {
315
this.player = this.add.sprite(400, 300, 'player');
316
317
// Basic shake
318
this.cameras.main.shake(500); // Duration in ms
319
320
// Shake with intensity
321
this.cameras.main.shake(1000, 0.05); // Intensity (0-1)
322
323
// Shake with direction
324
this.cameras.main.shake(300, 0.02, true); // Force horizontal shake
325
326
// Shake with callback
327
this.cameras.main.shake(500, 0.03, false, (camera, progress) => {
328
if (progress === 1) {
329
console.log('Shake complete');
330
}
331
});
332
333
// Shake events
334
this.cameras.main.on('camerashakestart', () => {
335
console.log('Shake started');
336
});
337
338
this.cameras.main.on('camerashakecomplete', () => {
339
console.log('Shake complete');
340
});
341
}
342
343
explosion(x, y) {
344
// Calculate shake intensity based on distance
345
const distance = Phaser.Math.Distance.Between(
346
this.cameras.main.scrollX + 400,
347
this.cameras.main.scrollY + 300,
348
x, y
349
);
350
351
const intensity = Phaser.Math.Clamp(1 - (distance / 500), 0, 0.1);
352
353
if (intensity > 0) {
354
this.cameras.main.shake(300, intensity);
355
}
356
}
357
}
358
```
359
360
### Pan Effects
361
Smooth camera panning to targets:
362
363
```javascript { .api }
364
class CameraPanScene extends Phaser.Scene {
365
create() {
366
this.player = this.add.sprite(100, 300, 'player');
367
this.treasure = this.add.sprite(700, 200, 'treasure');
368
369
// Pan to coordinates
370
this.cameras.main.pan(700, 200, 2000); // x, y, duration
371
372
// Pan with easing
373
this.cameras.main.pan(400, 300, 1500, 'Power2', false, (camera, progress, x, y) => {
374
if (progress === 1) {
375
console.log('Pan complete');
376
}
377
});
378
379
// Pan events
380
this.cameras.main.on('camerapanstart', () => {
381
console.log('Pan started');
382
});
383
384
this.cameras.main.on('camerapancomplete', () => {
385
console.log('Pan complete');
386
});
387
}
388
389
showTreasure() {
390
// Pan to treasure location
391
this.cameras.main.stopFollow(); // Stop following player
392
this.cameras.main.pan(this.treasure.x, this.treasure.y, 1000);
393
394
this.cameras.main.once('camerapancomplete', () => {
395
// Wait a moment then return to player
396
this.time.delayedCall(2000, () => {
397
this.cameras.main.pan(this.player.x, this.player.y, 1000);
398
this.cameras.main.once('camerapancomplete', () => {
399
this.cameras.main.startFollow(this.player);
400
});
401
});
402
});
403
}
404
}
405
```
406
407
### Zoom Effects
408
Smooth camera zooming:
409
410
```javascript { .api }
411
class CameraZoomScene extends Phaser.Scene {
412
create() {
413
this.player = this.add.sprite(400, 300, 'player');
414
415
// Zoom to level
416
this.cameras.main.zoomTo(2, 1000); // Zoom level, duration
417
418
// Zoom with easing and callback
419
this.cameras.main.zoomTo(0.5, 2000, 'Power2', false, (camera, progress, zoom) => {
420
if (progress === 1) {
421
console.log('Zoom complete, final zoom:', zoom);
422
}
423
});
424
425
// Zoom events
426
this.cameras.main.on('camerazoomstart', () => {
427
console.log('Zoom started');
428
});
429
430
this.cameras.main.on('camerazoomcomplete', () => {
431
console.log('Zoom complete');
432
});
433
}
434
435
enterBossArea() {
436
// Zoom out to show boss arena
437
this.cameras.main.zoomTo(0.7, 1500, 'Power2');
438
this.cameras.main.pan(this.bossArena.x, this.bossArena.y, 1500);
439
}
440
441
focusOnPlayer() {
442
// Zoom in for dramatic effect
443
this.cameras.main.zoomTo(1.5, 1000, 'Back.easeOut');
444
}
445
}
446
```
447
448
### Rotation Effects
449
Rotate camera view:
450
451
```javascript { .api }
452
class CameraRotationScene extends Phaser.Scene {
453
create() {
454
this.player = this.add.sprite(400, 300, 'player');
455
456
// Rotate to angle
457
this.cameras.main.rotateTo(0.5, false, 1000); // angle, shortestPath, duration
458
459
// Rotate with easing and callback
460
this.cameras.main.rotateTo(-0.3, true, 2000, 'Sine.easeInOut', false, (camera, progress, angle) => {
461
if (progress === 1) {
462
console.log('Rotation complete, final angle:', angle);
463
}
464
});
465
466
// Rotation events
467
this.cameras.main.on('camerarotatestart', () => {
468
console.log('Rotation started');
469
});
470
471
this.cameras.main.on('camerarotatecomplete', () => {
472
console.log('Rotation complete');
473
});
474
}
475
476
earthquake() {
477
// Random rotation for earthquake effect
478
const angle = Phaser.Math.FloatBetween(-0.1, 0.1);
479
this.cameras.main.rotateTo(angle, false, 100);
480
481
this.cameras.main.once('camerarotatecomplete', () => {
482
this.cameras.main.rotateTo(0, false, 100);
483
});
484
}
485
}
486
```
487
488
## Camera Controls
489
490
### Built-in Camera Controls
491
Phaser provides pre-built camera control schemes:
492
493
```javascript { .api }
494
class CameraControlsScene extends Phaser.Scene {
495
create() {
496
// Smoothed key control
497
const controlConfig = {
498
camera: this.cameras.main,
499
left: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A),
500
right: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D),
501
up: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W),
502
down: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S),
503
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
504
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
505
acceleration: 0.06,
506
drag: 0.0005,
507
maxSpeed: 1.0,
508
zoomSpeed: 0.02
509
};
510
511
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
512
513
// Fixed key control (immediate response)
514
const fixedConfig = {
515
camera: this.cameras.main,
516
left: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A),
517
right: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D),
518
up: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W),
519
down: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S),
520
speed: 0.5
521
};
522
523
this.fixedControls = new Phaser.Cameras.Controls.FixedKeyControl(fixedConfig);
524
}
525
526
update(time, delta) {
527
// Update camera controls
528
if (this.controls) {
529
this.controls.update(delta);
530
}
531
}
532
}
533
```
534
535
### Custom Camera Controls
536
Create custom camera control systems:
537
538
```javascript { .api }
539
class CustomCameraControlsScene extends Phaser.Scene {
540
create() {
541
this.cameras.main.setZoom(1);
542
543
// Mouse drag camera
544
this.input.on('pointerdown', () => {
545
this.isDragging = true;
546
this.dragStartX = this.input.activePointer.x;
547
this.dragStartY = this.input.activePointer.y;
548
this.cameraStartX = this.cameras.main.scrollX;
549
this.cameraStartY = this.cameras.main.scrollY;
550
});
551
552
this.input.on('pointermove', (pointer) => {
553
if (this.isDragging) {
554
const dragX = pointer.x - this.dragStartX;
555
const dragY = pointer.y - this.dragStartY;
556
557
this.cameras.main.setScroll(
558
this.cameraStartX - dragX,
559
this.cameraStartY - dragY
560
);
561
}
562
});
563
564
this.input.on('pointerup', () => {
565
this.isDragging = false;
566
});
567
568
// Edge scrolling
569
this.input.on('pointermove', (pointer) => {
570
const edgeThreshold = 50;
571
const scrollSpeed = 5;
572
573
if (pointer.x < edgeThreshold) {
574
this.cameras.main.scrollX -= scrollSpeed;
575
} else if (pointer.x > this.game.config.width - edgeThreshold) {
576
this.cameras.main.scrollX += scrollSpeed;
577
}
578
579
if (pointer.y < edgeThreshold) {
580
this.cameras.main.scrollY -= scrollSpeed;
581
} else if (pointer.y > this.game.config.height - edgeThreshold) {
582
this.cameras.main.scrollY += scrollSpeed;
583
}
584
});
585
586
// Touch pinch zoom
587
this.setupPinchZoom();
588
}
589
590
setupPinchZoom() {
591
let initialDistance = 0;
592
let initialZoom = 1;
593
594
this.input.on('pointerdown', (pointer) => {
595
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
596
initialDistance = Phaser.Math.Distance.Between(
597
this.input.pointer1.x, this.input.pointer1.y,
598
this.input.pointer2.x, this.input.pointer2.y
599
);
600
initialZoom = this.cameras.main.zoom;
601
}
602
});
603
604
this.input.on('pointermove', () => {
605
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
606
const currentDistance = Phaser.Math.Distance.Between(
607
this.input.pointer1.x, this.input.pointer1.y,
608
this.input.pointer2.x, this.input.pointer2.y
609
);
610
611
const scale = currentDistance / initialDistance;
612
const newZoom = initialZoom * scale;
613
614
this.cameras.main.setZoom(Phaser.Math.Clamp(newZoom, 0.5, 3));
615
}
616
});
617
}
618
}
619
```
620
621
## Advanced Camera Features
622
623
### Camera Culling
624
Optimize performance by culling off-screen objects:
625
626
```javascript { .api }
627
class CameraCullingScene extends Phaser.Scene {
628
create() {
629
// Disable culling for specific objects
630
this.cameras.main.disableCull = false; // Enable culling (default)
631
632
// Objects outside camera view won't be rendered
633
this.backgroundObjects = this.add.group();
634
635
// Force objects to always render (ignore culling)
636
this.ui = this.add.text(10, 10, 'Score: 0');
637
this.ui.setScrollFactor(0); // UI doesn't scroll with camera
638
639
// Custom culling for specific objects
640
this.particles = this.add.particles(0, 0, 'particle');
641
642
// Check if object is visible to camera
643
const isVisible = this.cameras.main.worldView.contains(this.player.x, this.player.y);
644
645
// Get camera world view
646
const worldView = this.cameras.main.worldView;
647
console.log('Camera world view:', worldView);
648
}
649
650
update() {
651
// Manual culling for performance
652
this.backgroundObjects.children.entries.forEach(obj => {
653
const inView = this.cameras.main.worldView.contains(obj.x, obj.y);
654
obj.setVisible(inView);
655
});
656
}
657
}
658
```
659
660
### Camera Masks
661
Use masks to create interesting visual effects:
662
663
```javascript { .api }
664
class CameraMaskScene extends Phaser.Scene {
665
create() {
666
// Create mask shape
667
const maskShape = this.add.graphics();
668
maskShape.fillStyle(0xffffff);
669
maskShape.fillCircle(400, 300, 150);
670
671
// Apply mask to camera
672
const mask = maskShape.createGeometryMask();
673
this.cameras.main.setMask(mask);
674
675
// Bitmap mask
676
const bitmapMask = this.add.image(400, 300, 'maskTexture');
677
const mask2 = bitmapMask.createBitmapMask();
678
this.cameras.main.setMask(mask2);
679
680
// Clear mask
681
this.cameras.main.clearMask();
682
683
// Animate mask
684
this.tweens.add({
685
targets: maskShape,
686
scaleX: 2,
687
scaleY: 2,
688
duration: 2000,
689
yoyo: true,
690
repeat: -1
691
});
692
}
693
}
694
```
695
696
This comprehensive camera system provides all the tools needed to create dynamic, engaging visual experiences with smooth movement, dramatic effects, and flexible viewport management.