Physics and Collision
Implement physics-based movement, gravity, and collision detection in Phaser games.
What you'll accomplish
Create a platformer with physics-based movement and collision.
Prerequisites
- Understanding of sprites and animations
- Completed previous Phaser lessons
Physics Systems in Phaser
Phaser includes two physics systems:
| System | Best For | Complexity |
|---|---|---|
| Arcade | Most 2D games, platformers | Simple |
| Matter.js | Realistic physics, complex shapes | Advanced |
We'll focus on Arcade Physics - it handles most game scenarios.
Enabling Arcade Physics
Configure in game config:
typescriptconst config: Phaser.Types.Core.GameConfig = { type: Phaser.AUTO, width: 800, height: 600, physics: { default: 'arcade', arcade: { gravity: { x: 0, y: 300 }, // World gravity debug: true, // Show collision boxes }, }, scene: [GameScene], };
Physics Bodies
To use physics, objects need a physics body:
typescriptcreate(): void { // Static body (doesn't move) const ground = this.physics.add.staticImage(400, 580, 'ground'); // Dynamic body (affected by physics) const player = this.physics.add.sprite(100, 300, 'player'); // Or add physics to existing object const coin = this.add.image(200, 100, 'coin'); this.physics.add.existing(coin); }
Static vs Dynamic
| Type | Moves | Affected by Gravity | Use For |
|---|---|---|---|
| Static | No | No | Ground, walls, platforms |
| Dynamic | Yes | Yes | Player, enemies, items |
Body Properties
typescriptconst player = this.physics.add.sprite(100, 300, 'player'); const body = player.body as Phaser.Physics.Arcade.Body; // Velocity (current movement) body.setVelocity(100, -200); body.setVelocityX(100); body.setVelocityY(-200); // Max velocity (speed limit) body.setMaxVelocity(300, 500); // Acceleration body.setAcceleration(50, 0); // Drag (friction/slowdown) body.setDrag(100, 0); // Bounce (0-1, how bouncy) player.setBounce(0.2); // Gravity (per-body) body.setGravityY(500); // Mass (affects collisions) body.setMass(1); // Stay in bounds player.setCollideWorldBounds(true); // Immovable (won't be pushed) body.setImmovable(true); // Collision box size body.setSize(24, 40); body.setOffset(4, 8); // Circular collision body.setCircle(20);
Collision Detection
Collider (Physical Collision)
Objects bounce off each other:
typescriptcreate(): void { const player = this.physics.add.sprite(100, 300, 'player'); const ground = this.physics.add.staticImage(400, 580, 'ground'); // Player collides with ground this.physics.add.collider(player, ground); // With callback this.physics.add.collider(player, enemies, (player, enemy) => { // Called when they collide this.playerHit(enemy); }); }
Overlap (Detect but Don't Collide)
Objects pass through each other:
typescriptcreate(): void { const player = this.physics.add.sprite(100, 300, 'player'); const coins = this.physics.add.group(); // Detect overlap this.physics.add.overlap(player, coins, (player, coin) => { coin.destroy(); this.score += 10; }); }
Groups
Manage multiple similar objects:
typescriptcreate(): void { // Create a group this.coins = this.physics.add.group(); // Add objects to group for (let i = 0; i < 10; i++) { const coin = this.coins.create(100 + i * 70, 100, 'coin'); coin.setBounce(0.5); } // Static group (platforms, ground) this.platforms = this.physics.add.staticGroup(); this.platforms.create(400, 580, 'ground'); this.platforms.create(200, 400, 'platform'); // Collision with group this.physics.add.collider(this.player, this.platforms); this.physics.add.overlap(this.player, this.coins, this.collectCoin, null, this); } collectCoin(player: Phaser.Physics.Arcade.Sprite, coin: Phaser.Physics.Arcade.Sprite): void { coin.disableBody(true, true); // Disable and hide this.score += 10; }
Platformer Example
A complete platformer scene:
typescriptimport Phaser from 'phaser'; export class PlatformerScene extends Phaser.Scene { private player!: Phaser.Physics.Arcade.Sprite; private platforms!: Phaser.Physics.Arcade.StaticGroup; private cursors!: Phaser.Types.Input.Keyboard.CursorKeys; constructor() { super('PlatformerScene'); } preload(): void { // For testing, create simple rectangles this.load.image('ground', 'assets/ground.png'); this.load.spritesheet('player', 'assets/player.png', { frameWidth: 32, frameHeight: 48, }); } create(): void { // Create platforms this.platforms = this.physics.add.staticGroup(); // Ground this.platforms.create(400, 584, 'ground').setScale(2).refreshBody(); // Floating platforms this.platforms.create(600, 400, 'ground'); this.platforms.create(50, 250, 'ground'); this.platforms.create(750, 220, 'ground'); // Create player this.player = this.physics.add.sprite(100, 450, 'player'); this.player.setBounce(0.1); this.player.setCollideWorldBounds(true); // Player animations this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }), frameRate: 10, repeat: -1, }); this.anims.create({ key: 'idle', frames: [{ key: 'player', frame: 4 }], frameRate: 20, }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('player', { start: 5, end: 8 }), frameRate: 10, repeat: -1, }); // Collision this.physics.add.collider(this.player, this.platforms); // Input this.cursors = this.input.keyboard!.createCursorKeys(); } update(): void { const body = this.player.body as Phaser.Physics.Arcade.Body; if (this.cursors.left.isDown) { body.setVelocityX(-160); this.player.anims.play('left', true); } else if (this.cursors.right.isDown) { body.setVelocityX(160); this.player.anims.play('right', true); } else { body.setVelocityX(0); this.player.anims.play('idle'); } // Jump only when touching ground if (this.cursors.up.isDown && body.blocked.down) { body.setVelocityY(-400); } } }
Checking Collision State
typescriptupdate(): void { const body = this.player.body as Phaser.Physics.Arcade.Body; // Check what the body is touching if (body.blocked.down) { // Standing on something } if (body.blocked.left || body.blocked.right) { // Touching a wall } if (body.touching.none) { // In the air } // Is body moving? if (body.velocity.x !== 0 || body.velocity.y !== 0) { // Moving } }
World Bounds
typescriptcreate(): void { // Set world size (can be larger than screen) this.physics.world.setBounds(0, 0, 1600, 600); // Player stays in bounds this.player.setCollideWorldBounds(true); // Camera follows player this.cameras.main.setBounds(0, 0, 1600, 600); this.cameras.main.startFollow(this.player); // World bounds collision events this.physics.world.on('worldbounds', (body: Phaser.Physics.Arcade.Body) => { console.log('Hit world bounds!'); }); // Enable worldbounds event on body this.player.body.onWorldBounds = true; }
Tilemaps with Physics
For level design, use tilemaps:
typescriptpreload(): void { this.load.image('tiles', 'assets/tileset.png'); this.load.tilemapTiledJSON('level1', 'assets/level1.json'); } create(): void { // Create tilemap const map = this.make.tilemap({ key: 'level1' }); const tileset = map.addTilesetImage('tileset', 'tiles'); // Create layers const backgroundLayer = map.createLayer('Background', tileset, 0, 0); const groundLayer = map.createLayer('Ground', tileset, 0, 0); // Enable collision for ground layer groundLayer.setCollisionByProperty({ collides: true }); // Or by tile index groundLayer.setCollisionBetween(1, 10); // Player collides with ground layer this.physics.add.collider(this.player, groundLayer); }
Tip
Use Tiled Map Editor (free) to create tilemaps visually.
Common Issues
Frequently Asked Questions
- Make sure platforms are
staticGroupor havesetImmovable(true) - Check that collider is set up:
this.physics.add.collider(player, platforms) - Ensure sprites are loaded correctly
Check if player is on ground before jumping:
typescriptif (this.cursors.up.isDown && body.blocked.down) { body.setVelocityY(-400); }
Adjust the body size and offset:
typescriptbody.setSize(24, 40); body.setOffset(4, 8);
Use debug: true to visualize collision boxes.
Common causes:
- Overlapping static bodies
- Too high gravity with low collision response
- Try increasing friction/drag
Check callback signature - Phaser passes objects, not just sprites:
typescriptthis.physics.add.overlap(player, coins, (p, c) => { // p and c are the colliding objects }, null, this);
Verify Your Understanding
You've completed this lesson if you can:
Next: Add sound effects and music to your game.