0
# Physics Systems
1
2
Phaser provides two powerful physics engines: Arcade Physics for fast, simple 2D collision detection, and Matter.js for realistic physics simulation. Choose the system that best fits your game's requirements.
3
4
## Arcade Physics
5
6
### World Setup
7
Arcade Physics provides a lightweight system perfect for most 2D games:
8
9
```javascript { .api }
10
// Game configuration with Arcade Physics
11
const config = {
12
type: Phaser.AUTO,
13
width: 800,
14
height: 600,
15
physics: {
16
default: 'arcade',
17
arcade: {
18
gravity: { x: 0, y: 300 },
19
debug: false,
20
debugShowBody: true,
21
debugShowStaticBody: true,
22
debugShowVelocity: true,
23
debugVelocityColor: 0x00ff00,
24
debugBodyColor: 0xff0000,
25
debugStaticBodyColor: 0x0000ff
26
}
27
},
28
scene: GameScene
29
};
30
31
class GameScene extends Phaser.Scene {
32
create() {
33
// Access physics world
34
const world = this.physics.world;
35
36
// World properties
37
console.log('Gravity:', world.gravity.x, world.gravity.y);
38
console.log('Bounds:', world.bounds);
39
console.log('Bodies count:', world.bodies.size);
40
41
// World bounds collision
42
world.setBoundsCollision(true, true, true, false); // left, right, top, bottom
43
44
// Custom world bounds
45
world.setBounds(0, 0, 1200, 800);
46
47
// Pause/resume physics
48
world.pause();
49
world.resume();
50
}
51
}
52
```
53
54
### Physics Bodies
55
Add physics to game objects:
56
57
```javascript { .api }
58
class PhysicsBodiesScene extends Phaser.Scene {
59
create() {
60
// Create physics-enabled sprites
61
this.player = this.physics.add.sprite(100, 100, 'player');
62
this.enemy = this.physics.add.sprite(300, 100, 'enemy');
63
64
// Static bodies (don't move)
65
this.platform = this.physics.add.staticSprite(400, 500, 'platform');
66
67
// Add physics to existing sprite
68
this.existingSprite = this.add.sprite(200, 200, 'item');
69
this.physics.add.existing(this.existingSprite);
70
71
// Body configuration
72
this.player.setCollideWorldBounds(true);
73
this.player.setBounce(0.2);
74
this.player.setDrag(100);
75
76
// Body size and offset
77
this.player.body.setSize(20, 30); // Custom collision size
78
this.player.body.setOffset(6, 2); // Offset from sprite origin
79
this.player.body.setCircle(15); // Circular collision shape
80
81
// Velocity
82
this.player.setVelocity(100, -300);
83
this.player.setVelocityX(150);
84
this.player.setVelocityY(-200);
85
86
// Acceleration
87
this.player.setAcceleration(50, 0);
88
this.player.setMaxVelocity(200, 400);
89
90
// Angular motion
91
this.player.setAngularVelocity(90); // degrees per second
92
this.player.setAngularAcceleration(45);
93
this.player.setAngularDrag(30);
94
95
// Mass and immovable
96
this.player.body.mass = 2;
97
this.platform.body.immovable = true;
98
}
99
100
update() {
101
// Check body properties
102
console.log('Player velocity:', this.player.body.velocity);
103
console.log('On floor:', this.player.body.onFloor());
104
console.log('On wall:', this.player.body.onWall());
105
console.log('Blocked down:', this.player.body.blocked.down);
106
107
// Manual velocity control
108
if (this.cursors.left.isDown) {
109
this.player.setVelocityX(-160);
110
} else if (this.cursors.right.isDown) {
111
this.player.setVelocityX(160);
112
} else {
113
this.player.setVelocityX(0);
114
}
115
116
// Jumping
117
if (this.cursors.up.isDown && this.player.body.touching.down) {
118
this.player.setVelocityY(-330);
119
}
120
}
121
}
122
```
123
124
### Collision Detection
125
Handle collisions between physics objects:
126
127
```javascript { .api }
128
class CollisionScene extends Phaser.Scene {
129
create() {
130
// Create game objects
131
this.player = this.physics.add.sprite(100, 450, 'player');
132
this.platforms = this.physics.add.staticGroup();
133
this.enemies = this.physics.add.group();
134
this.collectibles = this.physics.add.group();
135
136
// Add platforms
137
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();
138
this.platforms.create(600, 400, 'platform');
139
this.platforms.create(50, 250, 'platform');
140
141
// Basic collision
142
this.physics.add.collider(this.player, this.platforms);
143
144
// Collision with callback
145
this.physics.add.collider(this.player, this.enemies, this.hitEnemy, null, this);
146
147
// Overlap detection (no collision response)
148
this.physics.add.overlap(this.player, this.collectibles, this.collectItem, null, this);
149
150
// Collision with process callback (return true to separate)
151
this.physics.add.collider(this.player, this.enemies,
152
this.hitEnemy, // Collision callback
153
this.processHit, // Process callback (can prevent collision)
154
this
155
);
156
157
// Group vs group collisions
158
this.physics.add.collider(this.enemies, this.platforms);
159
this.physics.add.collider(this.enemies, this.enemies);
160
161
// World bounds collision
162
this.physics.add.collider(this.player, this.physics.world.bounds);
163
}
164
165
hitEnemy(player, enemy) {
166
console.log('Player hit enemy!');
167
enemy.setTint(0xff0000);
168
169
// Bounce player away
170
if (player.x < enemy.x) {
171
player.setVelocityX(-200);
172
} else {
173
player.setVelocityX(200);
174
}
175
176
// Damage player
177
this.playerHealth -= 10;
178
}
179
180
processHit(player, enemy) {
181
// Only allow collision if player is attacking
182
return this.playerIsAttacking;
183
}
184
185
collectItem(player, item) {
186
item.destroy();
187
this.score += 10;
188
189
// Play collection effect
190
this.tweens.add({
191
targets: player,
192
scaleX: 1.2,
193
scaleY: 1.2,
194
duration: 100,
195
yoyo: true
196
});
197
}
198
}
199
```
200
201
### Groups and Physics
202
Manage collections of physics objects:
203
204
```javascript { .api }
205
class PhysicsGroupsScene extends Phaser.Scene {
206
create() {
207
// Static group (platforms, walls)
208
this.platforms = this.physics.add.staticGroup();
209
this.platforms.create(400, 568, 'ground');
210
this.platforms.create(600, 400, 'platform');
211
212
// Dynamic group (moving objects)
213
this.enemies = this.physics.add.group({
214
key: 'enemy',
215
repeat: 5,
216
setXY: { x: 100, y: 0, stepX: 100 }
217
});
218
219
// Group with physics configuration
220
this.bullets = this.physics.add.group({
221
defaultKey: 'bullet',
222
maxSize: 20,
223
runChildUpdate: true,
224
createCallback: (bullet) => {
225
bullet.body.onWorldBounds = true;
226
bullet.body.world.on('worldbounds', (event, body) => {
227
if (body.gameObject === bullet) {
228
bullet.destroy();
229
}
230
});
231
}
232
});
233
234
// Group operations
235
this.enemies.children.entries.forEach(enemy => {
236
enemy.setBounce(1);
237
enemy.setCollideWorldBounds(true);
238
enemy.setVelocity(Phaser.Math.Between(-200, 200), 20);
239
});
240
241
// Batch physics properties
242
Phaser.Actions.Call(this.enemies.children.entries, (enemy) => {
243
enemy.setTint(Math.random() * 0xffffff);
244
});
245
246
// Create objects in group
247
const newEnemy = this.enemies.create(300, 200, 'enemy');
248
newEnemy.setVelocity(100, -100);
249
250
// Get objects from group
251
const firstEnemy = this.enemies.getFirst();
252
const randomEnemy = this.enemies.getRandom();
253
254
// Remove from group
255
this.enemies.remove(newEnemy);
256
this.enemies.killAndHide(randomEnemy);
257
}
258
}
259
```
260
261
### Advanced Arcade Features
262
Additional Arcade Physics capabilities:
263
264
```javascript { .api }
265
class AdvancedArcadeScene extends Phaser.Scene {
266
create() {
267
this.player = this.physics.add.sprite(100, 450, 'player');
268
this.platforms = this.physics.add.staticGroup();
269
270
// One-way platforms
271
const oneWayPlatform = this.platforms.create(400, 400, 'platform');
272
oneWayPlatform.body.checkCollision.down = false;
273
oneWayPlatform.body.checkCollision.left = false;
274
oneWayPlatform.body.checkCollision.right = false;
275
276
// Moving platforms
277
const movingPlatform = this.physics.add.image(200, 300, 'platform');
278
movingPlatform.setImmovable(true);
279
movingPlatform.body.kinematic = true;
280
movingPlatform.setVelocityX(50);
281
282
// Conveyor belt effect
283
const conveyorBelt = this.platforms.create(600, 500, 'conveyor');
284
this.physics.add.collider(this.player, conveyorBelt, (player, belt) => {
285
player.setVelocityX(player.body.velocity.x + 50);
286
});
287
288
// Slopes (requires custom collision handling)
289
this.slope = this.add.rectangle(400, 350, 200, 20, 0x00ff00);
290
this.physics.add.existing(this.slope, true); // Static body
291
292
// Custom separation
293
this.physics.add.overlap(this.player, this.slope, this.handleSlope, null, this);
294
295
// Body events
296
this.physics.world.on('worldbounds', (event, body) => {
297
console.log('Body hit world bounds:', body.gameObject);
298
if (body.gameObject === this.player) {
299
this.playerFellOff();
300
}
301
});
302
303
// Custom physics step
304
this.physics.world.on('worldstep', () => {
305
// Custom physics logic runs every step
306
this.applyCustomForces();
307
});
308
}
309
310
handleSlope(player, slope) {
311
// Custom slope physics
312
const slopeAngle = Math.PI / 6; // 30 degrees
313
const gravity = this.physics.world.gravity.y;
314
315
if (player.body.touching.down) {
316
const slopeForce = Math.sin(slopeAngle) * gravity;
317
player.setAccelerationX(slopeForce);
318
}
319
}
320
321
applyCustomForces() {
322
// Apply wind force
323
if (this.windActive) {
324
this.player.body.velocity.x += this.windStrength;
325
}
326
327
// Apply water resistance
328
if (this.playerInWater) {
329
this.player.body.velocity.x *= 0.95;
330
this.player.body.velocity.y *= 0.95;
331
}
332
}
333
}
334
```
335
336
## Matter.js Physics
337
338
### Matter World Setup
339
Matter.js provides realistic physics simulation:
340
341
```javascript { .api }
342
// Game configuration with Matter.js
343
const config = {
344
type: Phaser.AUTO,
345
width: 800,
346
height: 600,
347
physics: {
348
default: 'matter',
349
matter: {
350
gravity: { x: 0, y: 1 },
351
debug: {
352
showAxes: false,
353
showAngleIndicator: false,
354
angleColor: 0xe81153,
355
showBody: true,
356
showStaticBody: true,
357
showVelocity: true,
358
bodyColor: 0xffffff,
359
bodyFillColor: 0xffffff,
360
bodyLineWidth: 1,
361
staticBodyColor: 0x0d177b,
362
velocityColor: 0x00aeef,
363
velocityLineWidth: 1,
364
velocityLineLength: 20
365
}
366
}
367
},
368
scene: MatterScene
369
};
370
371
class MatterScene extends Phaser.Scene {
372
create() {
373
// Access Matter world
374
const world = this.matter.world;
375
376
// World configuration
377
world.setBounds(0, 0, 800, 600);
378
world.disableGravity(); // Disable gravity
379
world.setGravity(0, 0.8); // Custom gravity
380
381
// Engine properties
382
const engine = world.engine;
383
engine.world.gravity.scale = 0.001; // Gravity scale
384
engine.timing.timeScale = 1; // Time scale
385
}
386
}
387
```
388
389
### Matter Bodies
390
Create various physics bodies:
391
392
```javascript { .api }
393
class MatterBodiesScene extends Phaser.Scene {
394
create() {
395
// Rectangle body
396
this.box = this.matter.add.rectangle(400, 200, 80, 80, {
397
frictionAir: 0.01,
398
friction: 0.1,
399
frictionStatic: 0.5,
400
restitution: 0.8,
401
density: 0.001
402
});
403
404
// Circle body
405
this.ball = this.matter.add.circle(100, 200, 30, {
406
restitution: 0.9,
407
density: 0.002
408
});
409
410
// Polygon body
411
this.triangle = this.matter.add.polygon(600, 200, 3, 40, {
412
angle: Math.PI / 6
413
});
414
415
// Trapezoid
416
this.trapezoid = this.matter.add.trapezoid(300, 300, 80, 50, 0.5);
417
418
// Complex shapes from vertices
419
const star = this.matter.add.fromVertices(500, 300, [
420
{ x: 0, y: -20 },
421
{ x: 6, y: -6 },
422
{ x: 20, y: -6 },
423
{ x: 10, y: 2 },
424
{ x: 16, y: 16 },
425
{ x: 0, y: 8 },
426
{ x: -16, y: 16 },
427
{ x: -10, y: 2 },
428
{ x: -20, y: -6 },
429
{ x: -6, y: -6 }
430
]);
431
432
// Sprite with Matter body
433
this.player = this.matter.add.sprite(200, 100, 'player', null, {
434
shape: {
435
type: 'circle',
436
radius: 16
437
}
438
});
439
440
// Image with physics
441
this.crate = this.matter.add.image(150, 300, 'crate');
442
443
// Static bodies
444
this.ground = this.matter.add.rectangle(400, 580, 800, 40, {
445
isStatic: true
446
});
447
448
// Sensors (trigger areas)
449
this.sensor = this.matter.add.rectangle(400, 100, 100, 50, {
450
isSensor: true,
451
render: {
452
fillStyle: 'rgba(255, 255, 0, 0.5)'
453
}
454
});
455
}
456
}
457
```
458
459
### Matter Constraints
460
Connect bodies with constraints:
461
462
```javascript { .api }
463
class MatterConstraintsScene extends Phaser.Scene {
464
create() {
465
// Create bodies
466
const bodyA = this.matter.add.rectangle(300, 200, 50, 50);
467
const bodyB = this.matter.add.rectangle(450, 200, 50, 50);
468
const anchor = this.matter.add.rectangle(400, 100, 20, 20, { isStatic: true });
469
470
// Basic constraint (rope/string)
471
this.matter.add.constraint(bodyA, bodyB, 100, 0.7);
472
473
// Constraint to world point
474
this.matter.add.worldConstraint(anchor, 0, 0, {
475
pointA: { x: 0, y: 0 },
476
pointB: { x: 0, y: 0 },
477
length: 200,
478
stiffness: 0.8
479
});
480
481
// Spring constraint
482
this.matter.add.constraint(bodyA, bodyB, 150, 0.1, {
483
damping: 0.1,
484
angularStiffness: 0,
485
render: {
486
lineWidth: 5,
487
strokeStyle: '#90C695',
488
type: 'spring'
489
}
490
});
491
492
// Pin constraint (fixed point)
493
this.matter.add.constraint(bodyA, anchor, 0, 1, {
494
pointA: { x: 0, y: 0 },
495
pointB: { x: 0, y: 0 }
496
});
497
498
// Mouse constraint (drag with mouse)
499
this.matter.add.mouseSpring({
500
length: 0.01,
501
stiffness: 0.1
502
});
503
504
// Chain of bodies
505
const chain = [];
506
for (let i = 0; i < 5; i++) {
507
chain.push(this.matter.add.rectangle(100 + i * 60, 300, 40, 40));
508
}
509
510
// Connect chain links
511
for (let i = 0; i < chain.length - 1; i++) {
512
this.matter.add.constraint(chain[i], chain[i + 1], 60, 0.9);
513
}
514
}
515
}
516
```
517
518
### Matter Events
519
Handle collision and constraint events:
520
521
```javascript { .api }
522
class MatterEventsScene extends Phaser.Scene {
523
create() {
524
// Create objects
525
this.ball = this.matter.add.circle(400, 100, 30);
526
this.ground = this.matter.add.rectangle(400, 580, 800, 40, { isStatic: true });
527
528
// Collision events
529
this.matter.world.on('collisionstart', (event) => {
530
event.pairs.forEach((pair) => {
531
const { bodyA, bodyB } = pair;
532
console.log('Collision started between:', bodyA.label, bodyB.label);
533
534
// Check specific collision
535
if ((bodyA === this.ball.body && bodyB === this.ground.body) ||
536
(bodyB === this.ball.body && bodyA === this.ground.body)) {
537
this.ballHitGround();
538
}
539
});
540
});
541
542
this.matter.world.on('collisionactive', (event) => {
543
// Collision is ongoing
544
event.pairs.forEach((pair) => {
545
console.log('Collision active');
546
});
547
});
548
549
this.matter.world.on('collisionend', (event) => {
550
// Collision ended
551
event.pairs.forEach((pair) => {
552
console.log('Collision ended');
553
});
554
});
555
556
// Constraint events
557
this.matter.world.on('constraintbreak', (event) => {
558
console.log('Constraint broke:', event.constraint);
559
});
560
561
// Before/after update events
562
this.matter.world.on('beforeupdate', () => {
563
// Custom physics logic before engine update
564
});
565
566
this.matter.world.on('afterupdate', () => {
567
// Custom physics logic after engine update
568
});
569
570
// Sleep events
571
this.matter.world.on('sleepstart', (event) => {
572
console.log('Body went to sleep:', event.source);
573
});
574
575
this.matter.world.on('sleepend', (event) => {
576
console.log('Body woke up:', event.source);
577
});
578
}
579
580
ballHitGround() {
581
// Create impact effect
582
this.tweens.add({
583
targets: this.ball,
584
scaleX: 1.2,
585
scaleY: 0.8,
586
duration: 100,
587
yoyo: true
588
});
589
}
590
}
591
```
592
593
### Advanced Matter Features
594
Complex Matter.js functionality:
595
596
```javascript { .api }
597
class AdvancedMatterScene extends Phaser.Scene {
598
create() {
599
// Composite bodies (multiple shapes as one body)
600
const carBody = this.matter.add.rectangle(400, 200, 100, 50);
601
const wheelA = this.matter.add.circle(350, 225, 25);
602
const wheelB = this.matter.add.circle(450, 225, 25);
603
604
// Create car composite
605
const car = this.matter.body.create({
606
parts: [carBody, wheelA, wheelB]
607
});
608
this.matter.world.add(car);
609
610
// Body modification
611
this.matter.body.scale(this.ball.body, 1.5, 1.5);
612
this.matter.body.rotate(this.box.body, Math.PI / 4);
613
this.matter.body.translate(this.triangle.body, { x: 100, y: -50 });
614
615
// Apply forces
616
this.matter.body.applyForce(this.ball.body,
617
this.ball.body.position,
618
{ x: 0.05, y: -0.1 }
619
);
620
621
// Set velocity directly
622
this.matter.body.setVelocity(this.ball.body, { x: 5, y: -10 });
623
this.matter.body.setAngularVelocity(this.box.body, 0.1);
624
625
// Body properties
626
this.matter.body.setMass(this.ball.body, 10);
627
this.matter.body.setDensity(this.box.body, 0.002);
628
this.matter.body.setInertia(this.triangle.body, Infinity); // Prevent rotation
629
630
// Collision filtering
631
const categoryA = 0x0001;
632
const categoryB = 0x0002;
633
const categoryC = 0x0004;
634
635
// Bodies in category A collide with B and C
636
this.ball.body.collisionFilter = {
637
category: categoryA,
638
mask: categoryB | categoryC
639
};
640
641
// Bodies in category B only collide with A
642
this.box.body.collisionFilter = {
643
category: categoryB,
644
mask: categoryA
645
};
646
647
// Raycast
648
const rayCast = this.matter.world.raycast(
649
{ x: 100, y: 100 }, // Start point
650
{ x: 700, y: 500 } // End point
651
);
652
653
if (rayCast.length > 0) {
654
console.log('Ray hit:', rayCast[0].body);
655
}
656
657
// Query region
658
const bodiesInRegion = this.matter.world.query({
659
x: 300, y: 200,
660
width: 200, height: 100
661
});
662
663
console.log('Bodies in region:', bodiesInRegion);
664
}
665
}
666
```
667
668
## Performance Optimization
669
670
### Physics Performance Tips
671
Optimize physics performance:
672
673
```javascript { .api }
674
class PhysicsOptimizationScene extends Phaser.Scene {
675
create() {
676
// Limit physics bodies
677
const maxBodies = 100;
678
679
// Use object pooling for bullets/particles
680
this.bulletPool = this.physics.add.group({
681
maxSize: 20,
682
runChildUpdate: true
683
});
684
685
// Disable bodies when off-screen
686
this.physics.world.on('worldstep', () => {
687
this.enemies.children.entries.forEach(enemy => {
688
if (!this.cameras.main.worldView.contains(enemy.x, enemy.y)) {
689
enemy.body.enable = false;
690
} else {
691
enemy.body.enable = true;
692
}
693
});
694
});
695
696
// Use static bodies for non-moving objects
697
const staticPlatforms = this.physics.add.staticGroup();
698
699
// Reduce physics iterations for better performance
700
// (Matter.js only)
701
if (this.matter) {
702
this.matter.world.engine.positionIterations = 6; // Default: 6
703
this.matter.world.engine.velocityIterations = 4; // Default: 4
704
this.matter.world.engine.constraintIterations = 2; // Default: 2
705
}
706
707
// Use collision categories to reduce collision checks
708
// (Matter.js only)
709
const playerCategory = 0x0001;
710
const enemyCategory = 0x0002;
711
const platformCategory = 0x0004;
712
713
// Sleep inactive bodies (Matter.js)
714
if (this.matter) {
715
this.matter.world.engine.enableSleeping = true;
716
}
717
}
718
}
719
```
720
721
This comprehensive physics system provides both simple collision detection for arcade-style games and realistic physics simulation for more complex interactions, giving developers the flexibility to choose the right tool for their game's needs.