Claude-skill-registry dev-phaser-fundamentals

Phaser game configuration, scenes, and lifecycle management

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-phaser-fundamentals" ~/.claude/skills/majiayu000-claude-skill-registry-dev-phaser-fundamentals && rm -rf "$T"
manifest: skills/data/dev-phaser-fundamentals/SKILL.md
source content

Phaser Fundamentals

"2D web games made simple – scenes, sprites, and physics."

When to Use This Skill

Use when:

  • Setting up a new Phaser game
  • Creating game scenes
  • Implementing the scene lifecycle (preload/create/update)
  • Configuring game settings (scale, physics, renderer)
  • Setting up the game container and canvas

Quick Start

import Phaser from "phaser";
import { MainScene } from "./scenes/MainScene";

const config: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  parent: "game-container",
  backgroundColor: "#000000",
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
  },
  physics: {
    default: "arcade",
    arcade: {
      gravity: { y: 300 },
      debug: false,
    },
  },
  scene: [MainScene],
};

new Phaser.Game(config);

Before/After: Manual DOM vs Phaser Pattern

❌ Before: Manual DOM Manipulation

// Manual game loop without Phaser
const canvas = document.getElementById("game") as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;

let playerX = 400;
let playerY = 300;

// Manual asset loading
const playerImg = new Image();
playerImg.src = "assets/player.png";
playerImg.onload = () => {
  gameLoop();
};

// Manual game loop
function gameLoop() {
  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw player
  ctx.drawImage(playerImg, playerX, playerY);

  // Handle input manually
  document.addEventListener("keydown", (e) => {
    if (e.key === "ArrowLeft") playerX -= 5;
    // ... more manual input handling
  });

  requestAnimationFrame(gameLoop);
}

Problems:

  • Manual asset loading (no preload queue)
  • No sprite management
  • Manual collision detection
  • No physics engine
  • Scene management is manual

✅ After: Phaser Pattern

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

  preload() {
    // Built-in asset loading with progress tracking
    this.load.image("player", "assets/player.png");
  }

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

    // Built-in input handling
    this.cursors = this.input.keyboard!.createCursorKeys();
  }

  update() {
    // Frame-independent movement
    if (this.cursors.left!.isDown) {
      this.player.setVelocityX(-160);
    }
    // Built-in physics handles position updates
  }
}

const config = {
  // Phaser manages the game loop, canvas, rendering
  scene: [GameScene],
  physics: { default: "arcade" },
};

new Phaser.Game(config);

Benefits:

  • Asset preloading with progress events
  • Built-in physics engine
  • Scene lifecycle management
  • Input handling abstraction
  • Optimized rendering pipeline

Decision Framework

NeedUse
Basic 2D game
Phaser.AUTO
renderer type
Physics platformerArcade physics with gravity
Physics puzzleMatter physics for realism
Responsive layout
Phaser.Scale.FIT
with
CENTER_BOTH
Multiple scenesArray of scene classes in config

Progressive Guide

Level 1: Basic Scene Setup

export class MainScene extends Phaser.Scene {
  constructor() {
    super({ key: "MainScene" });
  }

  preload() {
    // Load assets before create()
    this.load.image("player", "assets/player.png");
    this.load.image("background", "assets/bg.png");
  }

  create() {
    // Create game objects
    this.add.image(400, 300, "background");
    this.player = this.physics.add.sprite(400, 300, "player");
  }

  update(time: number, delta: number) {
    // Game loop (60fps)
    // delta = time since last frame in ms
  }
}

Level 2: Scene Configuration with Data

export class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: "GameScene", active: false });
  }

  init(data: { level: number; score: number }) {
    // Receive data from previous scene
    this.level = data.level || 1;
    this.score = data.score || 0;
  }

  preload() {
    // Load level-specific assets
    this.load.image(`level${this.level}`, `assets/level${this.level}.png`);
  }

  create() {
    // Setup game objects
    this.createPlayer();
    this.createEnemies();
  }

  update(time: number, delta: number) {
    this.updatePlayer();
    this.updateEnemies();
  }
}

Level 3: Game Config with Multiple Scenes

const config: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  parent: "game-container",
  backgroundColor: "#2d2d2d",
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    width: 800,
    height: 600,
  },
  physics: {
    default: "arcade",
    arcade: {
      gravity: { x: 0, y: 1000 },
      debug: false,
    },
  },
  scene: [BootScene, PreloadScene, TitleScene, GameScene, UIScene],
  pipeline: { CustomPipeline: CustomPipeline },
};

Level 4: Custom Game Config Extension

interface ExtendedGameConfig extends Phaser.Types.Core.GameConfig {
  customSettings: {
    maxPlayers: number;
    gameMode: "deathmatch" | "capture";
  };
}

class CustomGame extends Phaser.Game {
  constructor(config: ExtendedGameConfig) {
    super(config);
    this.customSettings = config.customSettings;
  }
}

Level 5: Scene Manager Control

export class MainScene extends Phaser.Scene {
  create() {
    // Scene transitions
    this.scene.start("GameScene", { level: 1 });

    // Launch parallel scene (UI overlay)
    this.scene.launch("UIScene");

    // Pause current scene
    this.scene.pause();

    // Sleep scene (stops update but keeps rendering)
    this.scene.sleep("BackgroundScene");

    // Stop and remove scene
    this.scene.stop("OldScene");
  }
}

Anti-Patterns

DON'T:

  • Load assets in
    create()
    - use
    preload()
  • Create new objects in
    update()
    - causes GC pressure
  • Use
    this.scene.restart()
    frequently - expensive operation
  • Forget to call
    super()
    in scene constructor
  • Use hardcoded screen dimensions - use scale manager
  • Mix physics and non-physics objects without planning

DO:

  • Preload all assets in
    preload()
    before use
  • Reuse objects with object pooling in
    update()
  • Use scene data for passing values between scenes
  • Clean up event listeners in
    shutdown()
    method
  • Use
    scale.manager
    for responsive sizing
  • Group related game objects with
    this.add.group()

Code Patterns

Scene Lifecycle Management

export class GameScene extends Phaser.Scene {
  private player!: Phaser.Physics.Arcade.Sprite;
  private enemies!: Phaser.GameObjects.Group;
  private cursors!: Phaser.Types.Input.Keyboard.CursorKeys;

  constructor() {
    super({ key: "GameScene" });
  }

  create() {
    // Always setup shutdown for cleanup
    this.events.once(Phaser.Scenes.Events.SHUTDOWN, this.shutdown, this);
  }

  shutdown() {
    // Clean up listeners
    this.input.keyboard!.off("keydown-ESC");
  }

  update(time: number, delta: number) {
    // Use delta for frame-independent movement
    const dt = delta / 1000; // Convert to seconds
  }
}

Scale Manager Pattern

const config: Phaser.Types.Core.GameConfig = {
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    width: 800,
    height: 600,
  },
};

// In scene, get scale info
this.scale.width; // Actual canvas width
this.scale.height; // Actual canvas height

Checklist

Before completing Phaser setup:

  • Game config has proper scale settings
  • Physics configured correctly (arcade or matter)
  • Scene keys are unique
  • Assets preloaded before use in create()
  • Update loop is performant (no object creation)
  • Shutdown handlers for cleanup
  • Responsive scaling configured
  • Scene transitions use proper data passing

Common Issues

IssueSolution
Assets not loadingCheck
preload()
runs before
create()
Physics not workingVerify physics config and body type
Scene not updatingCheck scene is
active: true
Canvas size wrongConfigure scale manager
Memory leaksClean up in
shutdown()

Reference