Claude Agent Skill · by Chongdashu

Phaser Gamedev

Install Phaser Gamedev skill for Claude Code from chongdashu/phaserjs-tinyswords.

Install
Terminal · npx
$npx skills add https://github.com/chongdashu/phaserjs-tinyswords --skill phaser-gamedev
Works with Paperclip

How Phaser Gamedev fits into a Paperclip company.

Phaser Gamedev drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md691 lines
Expand
---name: phaser-gamedevdescription: >  Build 2D games with Phaser 3 framework. Covers scene lifecycle, sprites, physics (Arcade/Matter),  tilemaps, animations, input handling, and game architecture. Trigger: "create phaser game",  "add phaser scene", "phaser sprite", "phaser physics", "game development with phaser".--- # 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**:1. **Scene-First Architecture**: Structure games around scenes, not global state2. **Composition Over Inheritance**: Build entities from game objects and components3. **Physics-Aware Design**: Choose physics system before coding collisions4. **Asset Pipeline Discipline**: Preload everything, reference by key5. **Frame-Rate Independence**: Use delta time, not frame counting --- ## Game Configuration Every Phaser game starts with a configuration object. ### Minimal Configuration ```javascriptconst config = {  type: Phaser.AUTO,           // WebGL with Canvas fallback  width: 800,  height: 600,  scene: [BootScene, GameScene]}; const game = new Phaser.Game(config);``` ### Full Configuration Pattern ```javascriptconst 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 ```javascriptclass 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 ```javascript// 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 scenesthis.scene.pause('GameScene');this.scene.resume('GameScene'); // Stop a scenethis.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 ```javascript// Images (static)this.add.image(400, 300, 'background'); // Sprites (can animate, physics-enabled)const player = this.add.sprite(100, 100, 'player'); // Textconst 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]); // Tilemapsconst map = this.make.tilemap({ key: 'level1' });``` ### Sprite Creation Patterns ```javascript// Basic spriteconst sprite = this.add.sprite(x, y, 'textureKey'); // Sprite with physics bodyconst sprite = this.physics.add.sprite(x, y, 'textureKey'); // From spritesheet frameconst sprite = this.add.sprite(x, y, 'sheet', frameIndex); // From atlasconst sprite = this.add.sprite(x, y, 'atlas', 'frameName');``` --- ## Physics Systems ### Arcade Physics (Recommended Default) Fast, simple physics for most 2D games. ```javascript// Enable physics on spritethis.physics.add.sprite(x, y, 'player'); // Or add physics to existing spritethis.physics.add.existing(sprite); // Configure bodysprite.body.setVelocity(200, 0);sprite.body.setBounce(0.5);sprite.body.setCollideWorldBounds(true);sprite.body.setGravityY(300); // Collision detectionthis.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 ```javascript// 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. ```javascript// Configphysics: {  default: 'matter',  matter: {    gravity: { y: 1 },    debug: true  }} // Create bodiesconst ball = this.matter.add.circle(400, 100, 25);const box = this.matter.add.rectangle(400, 400, 100, 50, { isStatic: true }); // Sprite with Matter bodyconst player = this.matter.add.sprite(100, 100, 'player');player.setFriction(0.005);player.setBounce(0.9);``` --- ## Input Handling ### Keyboard Input ```javascript// Cursor keysthis.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 keysthis.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); // Key eventsthis.input.keyboard.on('keydown-SPACE', () => {  this.fire();});``` ### Pointer/Mouse Input ```javascript// Click/tapthis.input.on('pointerdown', (pointer) => {  console.log(pointer.x, pointer.y);}); // Make object interactivesprite.setInteractive();sprite.on('pointerdown', () => {  sprite.setTint(0xff0000);});sprite.on('pointerup', () => {  sprite.clearTint();}); // Dragthis.input.setDraggable(sprite);this.input.on('drag', (pointer, obj, dragX, dragY) => {  obj.x = dragX;  obj.y = dragY;});``` --- ## Animations ### Creating Animations ```javascript// In create() - define oncethis.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 atlasthis.anims.create({  key: 'explode',  frames: this.anims.generateFrameNames('atlas', {    prefix: 'explosion_',    start: 1,    end: 8,    zeroPad: 2  }),  frameRate: 16,  hideOnComplete: true});``` ### Playing Animations ```javascript// Play animationsprite.anims.play('walk', true);  // true = ignore if already playing // Play oncesprite.anims.play('jump'); // Stopsprite.anims.stop(); // Animation eventssprite.on('animationcomplete', (anim, frame) => {  if (anim.key === 'die') {    sprite.destroy();  }});``` --- ## Asset Loading ### Preload Patterns ```javascriptpreload() {  // 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 ```javascriptclass 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 ```javascriptpreload() {  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 ```javascript// Spawn points from Tiled object layerconst spawnPoint = map.findObject('Objects', obj => obj.name === 'spawn');this.player = this.physics.add.sprite(spawnPoint.x, spawnPoint.y, 'player'); // Create objects from layerconst 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 ```javascript// main.jsimport 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 ```javascriptbody.setVelocity(x, y)body.setVelocityX(x)body.setBounce(x, y)body.setGravityY(y)body.setCollideWorldBounds(true)body.setImmovable(true)        // For static-like dynamic bodiesbody.setDrag(x, y)body.setMaxVelocity(x, y)``` ### Useful Scene Properties ```javascriptthis.cameras.main              // Main camerathis.physics.world             // Physics worldthis.input.keyboard            // Keyboard managerthis.sound                     // Audio managerthis.time                      // Time/clock managerthis.tweens                    // Tween managerthis.anims                     // Animation managerthis.registry                  // Cross-scene data storethis.data                      // Scene-specific data store``` ### Essential Events ```javascript// Scene eventsthis.events.on('pause', callback)this.events.on('resume', callback)this.events.on('shutdown', callback) // Physics eventsthis.physics.world.on('worldbounds', callback) // Game object eventssprite.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.**