---
id: sm-phaser-gamedev
name: "phaser-gamedev"
url: https://skills.yangsir.net/skill/sm-phaser-gamedev
author: chongdashu
domain: game-dev
tags: ["phaser", "2d-game-development", "javascript-game-engine", "html5-games", "game-design"]
install_count: 910
rating: 4.20 (20 reviews)
github: https://github.com/chongdashu/phaserjs-tinyswords
---

# phaser-gamedev

> 利用 Phaser 3 的场景架构和物理系统，快速构建精致的 2D 浏览器游戏，强调游戏作为生命系统。

**Stats**: 910 installs · 4.2/5 (20 reviews)

## Before / After 对比

### 快速开发高质量2D浏览器游戏

## Readme

# phaser-gamedev

# Phaser Game Development

Build fast, polished 2D browser games using Phaser 3's scene-based architecture and physics systems.

## Philosophy: Games as Living Systems

Games are not static UIs—they are **dynamic systems** where entities interact, state evolves, and player input drives everything. Before writing code, think architecturally.

**Before building, ask**:

- What **scenes** does this game need? (Boot, Menu, Game, Pause, GameOver)

- What **entities** exist and how do they interact?

- What **state** must persist across scenes?

- What **physics** model fits? (Arcade for speed, Matter for realism)

- What **input methods** will players use?

**Core principles**:

- **Scene-First Architecture**: Structure games around scenes, not global state

- **Composition Over Inheritance**: Build entities from game objects and components

- **Physics-Aware Design**: Choose physics system before coding collisions

- **Asset Pipeline Discipline**: Preload everything, reference by key

- **Frame-Rate Independence**: Use delta time, not frame counting

## Game Configuration

Every Phaser game starts with a configuration object.

### Minimal Configuration

```
const config = {
  type: Phaser.AUTO,           // WebGL with Canvas fallback
  width: 800,
  height: 600,
  scene: [BootScene, GameScene]
};

const game = new Phaser.Game(config);

```

### Full Configuration Pattern

```
const config = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  parent: 'game-container',    // DOM element ID
  backgroundColor: '#2d2d2d',

  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH
  },

  physics: {
    default: 'arcade',
    arcade: {
      gravity: { y: 300 },
      debug: false              // Enable during development
    }
  },

  scene: [BootScene, MenuScene, GameScene, GameOverScene]
};

```

### Physics System Choice

System
Use When

**Arcade**
Platformers, shooters, most 2D games. Fast, simple AABB collisions

**Matter**
Physics puzzles, ragdolls, realistic collisions. Slower, more accurate

**None**
Menu scenes, visual novels, card games

## Scene Architecture

Scenes are the fundamental organizational unit. Each scene has a lifecycle.

### Scene Lifecycle Methods

```
class GameScene extends Phaser.Scene {
  constructor() {
    super('GameScene');        // Scene key for reference
  }

  init(data) {
    // Called first. Receive data from previous scene
    this.level = data.level || 1;
  }

  preload() {
    // Load assets. Runs before create()
    this.load.image('player', 'assets/player.png');
    this.load.spritesheet('enemy', 'assets/enemy.png', {
      frameWidth: 32, frameHeight: 32
    });
  }

  create() {
    // Set up game objects, physics, input
    this.player = this.physics.add.sprite(100, 100, 'player');
    this.cursors = this.input.keyboard.createCursorKeys();
  }

  update(time, delta) {
    // Game loop. Called every frame
    // delta = milliseconds since last frame
    this.player.x += this.speed * (delta / 1000);
  }
}

```

### Scene Transitions

```
// Start a new scene (stops current)
this.scene.start('GameOverScene', { score: this.score });

// Launch scene in parallel (both run)
this.scene.launch('UIScene');

// Pause/resume scenes
this.scene.pause('GameScene');
this.scene.resume('GameScene');

// Stop a scene
this.scene.stop('UIScene');

```

### Recommended Scene Structure

```
scenes/
├── BootScene.js      # Asset loading, progress bar
├── MenuScene.js      # Title screen, options
├── GameScene.js      # Main gameplay
├── UIScene.js        # HUD overlay (launched parallel)
├── PauseScene.js     # Pause menu overlay
└── GameOverScene.js  # End screen, restart option

```

## Game Objects

Everything visible in Phaser is a Game Object.

### Common Game Objects

```
// Images (static)
this.add.image(400, 300, 'background');

// Sprites (can animate, physics-enabled)
const player = this.add.sprite(100, 100, 'player');

// Text
const score = this.add.text(16, 16, 'Score: 0', {
  fontSize: '32px',
  fill: '#fff'
});

// Graphics (draw shapes)
const graphics = this.add.graphics();
graphics.fillStyle(0xff0000);
graphics.fillRect(100, 100, 50, 50);

// Containers (group objects)
const container = this.add.container(400, 300, [sprite1, sprite2]);

// Tilemaps
const map = this.make.tilemap({ key: 'level1' });

```

### Sprite Creation Patterns

```
// Basic sprite
const sprite = this.add.sprite(x, y, 'textureKey');

// Sprite with physics body
const sprite = this.physics.add.sprite(x, y, 'textureKey');

// From spritesheet frame
const sprite = this.add.sprite(x, y, 'sheet', frameIndex);

// From atlas
const sprite = this.add.sprite(x, y, 'atlas', 'frameName');

```

## Physics Systems

### Arcade Physics (Recommended Default)

Fast, simple physics for most 2D games.

```
// Enable physics on sprite
this.physics.add.sprite(x, y, 'player');

// Or add physics to existing sprite
this.physics.add.existing(sprite);

// Configure body
sprite.body.setVelocity(200, 0);
sprite.body.setBounce(0.5);
sprite.body.setCollideWorldBounds(true);
sprite.body.setGravityY(300);

// Collision detection
this.physics.add.collider(player, platforms);
this.physics.add.overlap(player, coins, collectCoin, null, this);

function collectCoin(player, coin) {
  coin.disableBody(true, true);  // Remove from physics and hide
  this.score += 10;
}

```

### Physics Groups

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

// Dynamic group (enemies, bullets)
const enemies = this.physics.add.group({
  key: 'enemy',
  repeat: 5,
  setXY: { x: 100, y: 0, stepX: 70 }
});

enemies.children.iterate(enemy => {
  enemy.setBounce(Phaser.Math.FloatBetween(0.4, 0.8));
});

```

### Matter Physics

For realistic physics simulations.

```
// Config
physics: {
  default: 'matter',
  matter: {
    gravity: { y: 1 },
    debug: true
  }
}

// Create bodies
const ball = this.matter.add.circle(400, 100, 25);
const box = this.matter.add.rectangle(400, 400, 100, 50, { isStatic: true });

// Sprite with Matter body
const player = this.matter.add.sprite(100, 100, 'player');
player.setFriction(0.005);
player.setBounce(0.9);

```

## Input Handling

### Keyboard Input

```
// Cursor keys
this.cursors = this.input.keyboard.createCursorKeys();

// In update()
if (this.cursors.left.isDown) {
  player.setVelocityX(-160);
} else if (this.cursors.right.isDown) {
  player.setVelocityX(160);
}

if (this.cursors.up.isDown && player.body.touching.down) {
  player.setVelocityY(-330);  // Jump
}

// Custom keys
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

// Key events
this.input.keyboard.on('keydown-SPACE', () => {
  this.fire();
});

```

### Pointer/Mouse Input

```
// Click/tap
this.input.on('pointerdown', (pointer) => {
  console.log(pointer.x, pointer.y);
});

// Make object interactive
sprite.setInteractive();
sprite.on('pointerdown', () => {
  sprite.setTint(0xff0000);
});
sprite.on('pointerup', () => {
  sprite.clearTint();
});

// Drag
this.input.setDraggable(sprite);
this.input.on('drag', (pointer, obj, dragX, dragY) => {
  obj.x = dragX;
  obj.y = dragY;
});

```

## Animations

### Creating Animations

```
// In create() - define once
this.anims.create({
  key: 'walk',
  frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
  frameRate: 10,
  repeat: -1  // Loop forever
});

this.anims.create({
  key: 'jump',
  frames: [{ key: 'player', frame: 4 }],
  frameRate: 20
});

// From atlas
this.anims.create({
  key: 'explode',
  frames: this.anims.generateFrameNames('atlas', {
    prefix: 'explosion_',
    start: 1,
    end: 8,
    zeroPad: 2
  }),
  frameRate: 16,
  hideOnComplete: true
});

```

### Playing Animations

```
// Play animation
sprite.anims.play('walk', true);  // true = ignore if already playing

// Play once
sprite.anims.play('jump');

// Stop
sprite.anims.stop();

// Animation events
sprite.on('animationcomplete', (anim, frame) => {
  if (anim.key === 'die') {
    sprite.destroy();
  }
});

```

## Asset Loading

### Preload Patterns

```
preload() {
  // Images
  this.load.image('sky', 'assets/sky.png');

  // Spritesheets
  this.load.spritesheet('player', 'assets/player.png', {
    frameWidth: 32,
    frameHeight: 48
  });

  // Atlases (TexturePacker)
  this.load.atlas('sprites', 'assets/sprites.png', 'assets/sprites.json');

  // Tilemaps
  this.load.tilemapTiledJSON('map', 'assets/level1.json');
  this.load.image('tiles', 'assets/tileset.png');

  // Audio
  this.load.audio('bgm', 'assets/music.mp3');
  this.load.audio('sfx', ['assets/sound.ogg', 'assets/sound.mp3']);

  // Progress tracking
  this.load.on('progress', (value) => {
    console.log(`Loading: ${Math.round(value * 100)}%`);
  });
}

```

### Boot Scene Pattern

```
class BootScene extends Phaser.Scene {
  constructor() {
    super('BootScene');
  }

  preload() {
    // Loading bar
    const width = this.cameras.main.width;
    const height = this.cameras.main.height;

    const progressBar = this.add.graphics();
    const progressBox = this.add.graphics();
    progressBox.fillStyle(0x222222, 0.8);
    progressBox.fillRect(width/2 - 160, height/2 - 25, 320, 50);

    this.load.on('progress', (value) => {
      progressBar.clear();
      progressBar.fillStyle(0xffffff, 1);
      progressBar.fillRect(width/2 - 150, height/2 - 15, 300 * value, 30);
    });

    // Load all game assets here
    this.load.image('player', 'assets/player.png');
    // ... more assets
  }

  create() {
    this.scene.start('MenuScene');
  }
}

```

## Tilemaps (Tiled Integration)

### Loading and Creating

```
preload() {
  this.load.tilemapTiledJSON('map', 'assets/map.json');
  this.load.image('tiles', 'assets/tileset.png');
}

create() {
  const map = this.make.tilemap({ key: 'map' });
  const tileset = map.addTilesetImage('tileset-name-in-tiled', 'tiles');

  // Create layers (match names from Tiled)
  const backgroundLayer = map.createLayer('Background', tileset, 0, 0);
  const groundLayer = map.createLayer('Ground', tileset, 0, 0);

  // Enable collision on specific tiles
  groundLayer.setCollisionByProperty({ collides: true });
  // Or by tile index
  groundLayer.setCollisionBetween(1, 100);

  // Add collision with player
  this.physics.add.collider(this.player, groundLayer);
}

```

### Object Layers

```
// Spawn points from Tiled object layer
const spawnPoint = map.findObject('Objects', obj => obj.name === 'spawn');
this.player = this.physics.add.sprite(spawnPoint.x, spawnPoint.y, 'player');

// Create objects from layer
const coins = map.createFromObjects('Objects', {
  name: 'coin',
  key: 'coin'
});
this.physics.world.enable(coins);

```

## Project Structure

### Recommended Organization

```
game/
├── src/
│   ├── scenes/
│   │   ├── BootScene.js
│   │   ├── MenuScene.js
│   │   ├── GameScene.js
│   │   └── UIScene.js
│   ├── gameObjects/
│   │   ├── Player.js
│   │   ├── Enemy.js
│   │   └── Collectible.js
│   ├── systems/
│   │   ├── InputManager.js
│   │   └── AudioManager.js
│   ├── config/
│   │   └── gameConfig.js
│   └── main.js
├── assets/
│   ├── images/
│   ├── audio/
│   ├── tilemaps/
│   └── fonts/
├── index.html
└── package.json

```

### ES Module Setup

```
// main.js
import Phaser from 'phaser';
import BootScene from './scenes/BootScene';
import GameScene from './scenes/GameScene';
import { gameConfig } from './config/gameConfig';

const config = {
  ...gameConfig,
  scene: [BootScene, GameScene]
};

new Phaser.Game(config);

```

## Anti-Patterns to Avoid

❌ **Global State Soup**: Storing game state on `window` or module globals
**Why bad**: Untrackable bugs, scene transitions break state
**Better**: Use scene data, registries, or dedicated state managers

❌ **Loading in Create**: Loading assets in `create()` instead of `preload()`
**Why bad**: Assets may not be ready when referenced
**Better**: Always load in `preload()`, use Boot scene for all assets

❌ **Frame-Dependent Logic**: Using frame count instead of delta time
**Why bad**: Game speed varies with frame rate
**Better**: `this.speed * (delta / 1000)` for consistent movement

❌ **Physics Overkill**: Using Matter for simple platformer collisions
**Why bad**: Performance hit, unnecessary complexity
**Better**: Arcade physics handles 90% of 2D game needs

❌ **Monolithic Scenes**: One giant scene with all game logic
**Why bad**: Unmaintainable, hard to add features
**Better**: Separate scenes for menus, gameplay, UI overlays

❌ **Magic Numbers**: Hardcoded values scattered in code
**Why bad**: Impossible to balance, inconsistent
**Better**: Config objects, constants files

❌ **Ignoring Object Pooling**: Creating/destroying objects every frame
**Why bad**: Memory churn, garbage collection stutters
**Better**: Use groups with `setActive(false)` / `setVisible(false)`

❌ **Synchronous Asset Access**: Assuming assets load instantly
**Why bad**: Race conditions, undefined textures
**Better**: Chain scene starts, use load events

❌ **Assuming Spritesheet Frame Dimensions**: Using guessed frame sizes without verifying
**Why bad**: Wrong dimensions cause silent frame corruption; off-by-pixels compounds into broken visuals
**Better**: Open asset file, measure frames, calculate with spacing/margin, verify math adds up

❌ **Ignoring Spritesheet Spacing**: Not specifying `spacing` for gapped spritesheets
**Why bad**: Frames shift progressively; later frames read wrong pixel regions
**Better**: Check source asset for gaps between frames; use `spacing: N` in loader config

❌ **Hardcoding Nine-Slice Colors**: Using single background color for all UI panel variants
**Why bad**: Transparent frame edges reveal wrong color for different asset color schemes
**Better**: Per-asset background color config; sample from center frame (frame 4)

❌ **Nine-Slice with Padded Frames**: Treating the full frame as the slice region when the art is centered/padded inside each tile
**Why bad**: Edge tiles contribute interior fill, showing up as opaque “side bars” inside the panel
**Better**: Trim tiles to their effective content bounds (alpha bbox) and composite/cache a texture; add ~1px overlap + disable smoothing to avoid seams

❌ **Scaling Discontinuous UI Art**: Stretching a cropped ribbon/banner row that contains internal transparent gaps
**Why bad**: The transparent gutters get stretched, so the UI looks segmented or the fill disappears behind the frame.
**Better**: Slice the asset into caps/center, stretch only the center, and stitch the pieces (with ~1px overlap + smoothing disabled) before rendering at pivot sizes.

## Variation Guidance

**IMPORTANT**: Game implementations should vary based on:

- **Game Genre**: Platformer physics differ from top-down shooter physics

- **Target Platform**: Mobile needs touch input, desktop can use keyboard

- **Art Style**: Pixel art uses nearest-neighbor scaling, HD art uses linear

- **Performance Needs**: Many sprites → object pooling; few sprites → simple creation

- **Complexity**: Simple games can inline; complex games need class hierarchies

**Avoid converging on**:

- Always using 800x600 resolution

- Always using Arcade physics

- Always using the same scene structure

- Copy-pasting boilerplate without adaptation

## Quick Reference

### Common Physics Properties

```
body.setVelocity(x, y)
body.setVelocityX(x)
body.setBounce(x, y)
body.setGravityY(y)
body.setCollideWorldBounds(true)
body.setImmovable(true)        // For static-like dynamic bodies
body.setDrag(x, y)
body.setMaxVelocity(x, y)

```

### Useful Scene Properties

```
this.cameras.main              // Main camera
this.physics.world             // Physics world
this.input.keyboard            // Keyboard manager
this.sound                     // Audio manager
this.time                      // Time/clock manager
this.tweens                    // Tween manager
this.anims                     // Animation manager
this.registry                  // Cross-scene data store
this.data                      // Scene-specific data store

```

### Essential Events

```
// Scene events
this.events.on('pause', callback)
this.events.on('resume', callback)
this.events.on('shutdown', callback)

// Physics events
this.physics.world.on('worldbounds', callback)

// Game object events
sprite.on('destroy', callback)
sprite.on('animationcomplete', callback)

```

## See Also

- **references/arcade-physics.md** - Deep dive into Arcade physics

- **references/tilemaps.md** - Advanced tilemap techniques

- **references/performance.md** - Optimization strategies

- **references/spritesheets-nineslice.md** - Spritesheet loading (spacing/margin), nine-slice UI panels, asset inspection

## Remember

Phaser gives you powerful primitives—scenes, sprites, physics, input—but **architecture is your responsibility**.

Think in systems: What scenes do you need? What entities exist? How do they interact? Answer these questions before writing code, and your game will be maintainable as it grows.

**Claude is capable of building complete, polished Phaser games. These guidelines illuminate the path—they don't fence it.**
Weekly Installs487Repository[chongdashu/phas…nyswords](https://github.com/chongdashu/phaserjs-tinyswords)GitHub Stars29First SeenJan 25, 2026Security Audits[Gen Agent Trust HubPass](/chongdashu/phaserjs-tinyswords/phaser-gamedev/security/agent-trust-hub)[SocketPass](/chongdashu/phaserjs-tinyswords/phaser-gamedev/security/socket)[SnykPass](/chongdashu/phaserjs-tinyswords/phaser-gamedev/security/snyk)Installed onopencode431codex421gemini-cli415github-copilot386kimi-cli360amp356

---
*Source: https://skills.yangsir.net/skill/sm-phaser-gamedev*
*Markdown mirror: https://skills.yangsir.net/api/skill/sm-phaser-gamedev/markdown*