Agent skill

dev-phaser-physics-arcade

Arcade physics velocity, acceleration, collision, and bounds

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/dev-phaser-physics-arcade

SKILL.md

Phaser Arcade Physics

"Fast, simple 2D physics for platformers and arcade games."

Before/After: Manual Collision vs Phaser Arcade Physics

❌ Before: Manual AABB Collision

typescript
// Manual physics without Phaser
interface Box {
  x: number;
  y: number;
  width: number;
  height: number;
  vx: number;
  vy: number;
}

const player: Box = { x: 100, y: 300, width: 32, height: 48, vx: 0, vy: 0 };
const platforms: Box[] = [
  { x: 0, y: 500, width: 800, height: 68 }
];

function checkCollision(a: Box, b: Box): boolean {
  return a.x < b.x + b.width &&
         a.x + a.width > b.x &&
         a.y < b.y + b.height &&
         a.y + a.height > b.y;
}

function updatePhysics(dt: number) {
  // Apply gravity
  player.vy += 1000 * dt;

  // Update position
  player.x += player.vx * dt;
  player.y += player.vy * dt;

  // Check collision with each platform
  for (const platform of platforms) {
    if (checkCollision(player, platform)) {
      // Resolve collision
      if (player.vy > 0 && player.y + player.height - player.vy * dt <= platform.y) {
        player.y = platform.y - player.height;
        player.vy = 0;
      }
    }
  }

  // World bounds
  if (player.x < 0) player.x = 0;
  if (player.x > 800 - player.width) player.x = 800 - player.width;
}

// Problems:
// - Manual collision detection is error-prone
// - No proper physics simulation
// - Tunneling at high speeds
// - No built-in platformer features
// - Manual state tracking

✅ After: Phaser Arcade Physics

typescript
// Phaser handles physics automatically
export class GameScene extends Phaser.Scene {
  private player!: Phaser.Physics.Arcade.Sprite;

  create() {
    // Physics-enabled sprite with built-in collision
    this.player = this.physics.add.sprite(100, 450, 'player');
    this.player.setBounce(0); // No bounce
    this.player.setCollideWorldBounds(true);

    // Static platforms group
    const platforms = this.physics.add.staticGroup();
    platforms.create(400, 568, 'ground').setScale(2, 1).refreshBody();

    // One line to add collision
    this.physics.add.collider(this.player, platforms);
  }

  update() {
    // Simple velocity-based movement
    if (this.cursors.left.isDown) {
      this.player.setVelocityX(-160);
    } else if (this.cursors.right.isDown) {
      this.player.setVelocityX(160);
    } else {
      this.player.setVelocityX(0);
    }

    // Grounded check built-in
    if (this.cursors.up.isDown && this.player.body!.touching.down) {
      this.player.setVelocityY(-330);
    }
  }
}

// Benefits:
// - Built-in collision detection (no tunneling)
// - Touching detection (grounded, walls)
// - World bounds handling
// - Proper physics simulation
// - Easy platformer features

When to Use This Skill

Use when:

  • Implementing platformer games
  • Creating collision detection
  • Working with velocity and gravity
  • Building arcade-style gameplay
  • Need simple, performant physics

Quick Start

typescript
// Enable arcade physics in game config
physics: {
  default: 'arcade',
  arcade: {
    gravity: { y: 1000 },
    debug: false
  }
}

// Create physics sprite
const player = this.physics.add.sprite(400, 300, 'player');
player.setBounce(0.2);
player.setCollideWorldBounds(true);

Decision Framework

Need Use
Platformer Arcade with gravity
Top-down Arcade without gravity
Bouncing objects setBounce(0.5)
Static platforms this.physics.add.staticGroup()
Collision detection this.physics.add.collider()

Progressive Guide

Level 1: Basic Physics Body

typescript
export class GameScene extends Phaser.Scene {
  private player!: Phaser.Physics.Arcade.Sprite;

  create() {
    // Create physics sprite
    this.player = this.physics.add.sprite(400, 300, "player");

    // Physics properties
    this.player.setBounce(0.2); // Bounciness (0-1)
    this.player.setCollideWorldBounds(true); // Don't leave screen
    this.player.setDrag(0.1, 0); // Air resistance
    this.player.setFriction(0.1); // Ground friction
    this.player.setMaxVelocity(400); // Speed cap

    // Velocity control
    this.player.setVelocityX(200);
    this.player.setVelocityY(-300);
    this.player.setVelocity(100, 100);

    // Acceleration
    this.player.setAccelerationX(50);
  }
}

Level 2: Collision Detection

typescript
create() {
  const player = this.physics.add.sprite(400, 300, 'player');
  const platforms = this.physics.add.staticGroup();

  // Create platforms
  platforms.create(400, 568, 'ground').setScale(2, 1).refreshBody();
  platforms.create(600, 400, 'platform');

  // Player collides with platforms
  this.physics.add.collider(player, platforms);

  // Overlap detection (trigger, no collision response)
  const coins = this.physics.add.group({
    defaultKey: 'coin',
    bounceX: 0.5,
    bounceY: 0.5
  });

  this.physics.add.overlap(player, coins, this.collectCoin, undefined, this);
}

collectCoin(player: Phaser.Types.Physics.Arcade.GameObjectWithBody, coin: any) {
  coin.disableBody(true, true); // Disable and hide
}

Level 3: Platformer Controls

typescript
export class GameScene extends Phaser.Scene {
  private player!: Phaser.Physics.Arcade.Sprite;
  private cursors!: Phaser.Types.Input.Keyboard.CursorKeys;
  private jumpKey!: Phaser.Input.Keyboard.Key;

  create() {
    this.player = this.physics.add.sprite(100, 450, "player");
    this.player.setBounce(0);
    this.player.setCollideWorldBounds(true);

    // Setup inputs
    this.cursors = this.input.keyboard!.createCursorKeys();
    this.jumpKey = this.input.keyboard!.addKey("SPACE");

    // Platforms
    const platforms = this.physics.add.staticGroup();
    platforms.create(400, 568, "ground").setScale(2).refreshBody();

    this.physics.add.collider(this.player, platforms);
  }

  update() {
    // Left/Right movement
    if (this.cursors.left.isDown) {
      this.player.setVelocityX(-160);
    } else if (this.cursors.right.isDown) {
      this.player.setVelocityX(160);
    } else {
      this.player.setVelocityX(0);
    }

    // Jump only if touching ground
    if (this.jumpKey.isDown && this.player.body!.touching.down) {
      this.player.setVelocityY(-330);
    }
  }
}

Level 4: Advanced Collision Callbacks

typescript
create() {
  // Collision with process callback (custom collision logic)
  this.physics.add.collider(
    this.player,
    this.enemies,
    this.playerHitEnemy,
    this.checkEnemyVulnerable,
    this
  );
}

// Called to check if collision should happen
checkEnemyVulnerable(
  player: Phaser.Types.Physics.Arcade.GameObjectWithBody,
  enemy: any
): boolean {
  const enemySprite = enemy as Phaser.Physics.Arcade.Sprite;
  // Only collide if enemy is vulnerable
  return !enemySprite.getData('invincible');
}

// Called when collision occurs
playerHitEnemy(
  player: Phaser.Types.Physics.Arcade.GameObjectWithBody,
  enemy: any
) {
  const enemySprite = enemy as Phaser.Physics.Arcade.Sprite;

  // If player is above enemy, kill enemy
  if (player.body!.velocity.y > 0 && player.y < enemySprite.y - 10) {
    enemySprite.destroy();
    player.setVelocityY(-200); // Bounce
  } else {
    // Player dies
    this.handlePlayerDeath();
  }
}

Level 5: Custom Physics Components

typescript
// Physics component class
class PlatformerController {
  private speed = 160;
  private jumpForce = -330;
  private isGrounded = false;

  constructor(
    private sprite: Phaser.Physics.Arcade.Sprite,
    private cursors: Phaser.Types.Input.Keyboard.CursorKeys,
    private jumpKey: Phaser.Input.Keyboard.Key
  ) {
    this.enableDoubleJump();
  }

  update() {
    // Horizontal movement
    if (this.cursors.left.isDown) {
      this.sprite.setVelocityX(-this.speed);
      this.sprite.setFlipX(true);
    } else if (this.cursors.right.isDown) {
      this.sprite.setVelocityX(this.speed);
      this.sprite.setFlipX(false);
    } else {
      this.sprite.setVelocityX(0);
    }

    // Jump
    if (this.jumpKey.isDown && this.isGrounded) {
      this.sprite.setVelocityY(this.jumpForce);
      this.isGrounded = false;
    }
  }

  onGrounded() {
    this.isGrounded = true;
  }

  private enableDoubleJump() {
    this.jumpKey.on('down', () => {
      if (!this.isGrounded && this.sprite.body!.velocity.y > -200) {
        this.sprite.setVelocityY(this.jumpForce * 0.8);
      }
    });
  }
}

// In scene
create() {
  const controller = new PlatformerController(
    this.player,
    this.cursors,
    this.jumpKey
  );

  this.physics.add.collider(this.player, platforms, () => {
    controller.onGrounded();
  });
}

update() {
  controller.update();
}

Anti-Patterns

DON'T:

  • Use Matter physics for simple platformers - Arcade is faster
  • Set position directly on physics bodies - use velocity
  • Forget to call refreshBody() after scaling static objects
  • Use overlap when collider is needed
  • Disable physics bodies instead of removing from scene
  • Mix pixel and meter units arbitrarily

DO:

  • Use Arcade for platformers, top-down games
  • Apply forces/velocity for movement
  • Refresh body after scale/position changes
  • Use proper collision types (collider vs overlap)
  • Remove bodies from physics world when destroyed
  • Keep units consistent (pixels)

Code Patterns

One-Way Platforms

typescript
create() {
  const platforms = this.physics.add.staticGroup();

  // Create one-way platform
  const platform = platforms.create(400, 400, 'platform');
  platform.checkCollision.down = false; // Can jump through from below
  platform.checkCollision.up = true;    // Lands on top
}

Body Size Adjustment

typescript
const player = this.physics.add.sprite(400, 300, "player");

// Adjust body size (smaller than sprite)
player.body!.setSize(20, 40);
player.body!.setOffset(6, 8); // Center the body

// Disable body on one side
player.body!.checkCollision.up = false;

Moving Platform

typescript
export class MovingPlatform extends Phaser.Physics.Arcade.Image {
  constructor(
    scene: Phaser.Scene,
    x: number,
    y: number,
    texture: string,
    private startX: number,
    private endX: number,
  ) {
    super(scene, x, y, texture);
    scene.add.existing(this);
    scene.physics.add.existing(this, false); // false = static

    this.setVelocityX(50);
    this.scene.events.on("update", this.updatePlatform, this);
  }

  updatePlatform() {
    if (this.x >= this.endX) {
      this.setVelocityX(-50);
    } else if (this.x <= this.startX) {
      this.setVelocityX(50);
    }
  }
}

Checklist

  • Physics enabled in game config
  • Bodies properly sized (offset from sprite)
  • Static bodies use staticGroup()
  • Dynamic bodies use physics.add.sprite()
  • Colliders/overlaps configured
  • World bounds enabled if needed
  • Refresh body called after scale

Physics Body Properties

Property Type Description
velocity Vector2 Current velocity
acceleration Vector2 Applied acceleration
drag Vector2 Deceleration over time
gravity Vector2 Applied gravity force
bounce number Bounciness (0-1)
friction number Surface friction
maxVelocity Vector2 Maximum speed cap
allowGravity boolean Whether gravity affects body
immovable boolean Body cannot be pushed

Reference

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results