Phaser 3 Game Development Skill
Quick Start
Most Common Patterns
Multi-Scene Flow
const config = {
scene: [ Boot, Preloader, MainMenu, Game, GameOver ]
};
// Boot → Preloader → MainMenu → Game → GameOver
Object Pooling
create() {
this.projectiles = this.physics.add.group({
classType: Projectile,
frameQuantity: 20,
active: false,
visible: false
});
}
fire() {
const projectile = this.projectiles.getFirstDead(false);
if (projectile) projectile.fire(x, y);
}
Global Animations
// In Preloader.js create()
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
JustDown Input
if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
this.jump();
}
Navigation Guide
How to Use This Skill
This skill provides access to 18 comprehensive pattern files covering all aspects of Phaser 3 development. Each file contains detailed patterns, code examples, and best practices.
To find specific patterns:
# Read a specific topic Read knowledgebase/06-input-handling.md # Search for a pattern across all files grep -r "object pooling" knowledgebase/ # Find patterns by keyword grep -i "animation" knowledgebase/*.md grep -i "collision" knowledgebase/*.md
Quick lookup by number:
- •01 = Scene Architecture
- •02 = Asset Management
- •03 = Physics & Collision
- •04 = Movement Patterns
- •05 = Animation System
- •06 = Input Handling
- •07 = State Management
- •08 = Object Pooling & Memory
- •09 = Grid Systems
- •10 = Custom Game Objects
- •11 = UI/HUD Patterns
- •12 = Tween & Visual Effects
- •13 = Audio Integration
- •14 = Game Loop Patterns
- •15 = Algorithm Implementations
- •16 = Performance Optimization
- •17 = Code Organization
- •18 = Development Philosophy
Table of Contents
1. Scene Architecture
knowledgebase/01-scene-architecture.md
Multi-scene flow, parallel scenes for UI overlays, scene transitions with effects, data passing between scenes.
Key Patterns:
- •Boot → Preloader → MainMenu → Game → GameOver flow
- •Parallel UI scene with
scene.launch() - •Camera fade transitions
- •Scene data passing via
init(data)
2. Asset Management
knowledgebase/02-asset-management.md
Two-stage loading, texture atlases, multi-format audio, font synchronization.
Key Patterns:
- •Bootstrap assets in Boot, full assets in Preloader
- •Texture atlas for multiple sprites
- •Multi-format audio for browser compatibility
- •WebFont loading synchronization
3. Physics & Collision
knowledgebase/03-physics-collision.md
Physics configuration, collision vs overlap, dynamic responses, state-gated collision.
Key Patterns:
- •Collider for physical blocking, overlap for triggers
- •Position-based dynamic response (paddle hits)
- •State-gated collision checks
- •Per-track/layer collision isolation
4. Movement Patterns
knowledgebase/04-movement-patterns.md
Velocity-based, time-based discrete, timer-based, pursuit, direction validation, screen wrapping.
Key Patterns:
- •Velocity-based for continuous action games
- •Time-based discrete for grid games
- •Timer-based for turn-based games
- •Direction validation (prevent 180° turns)
5. Animation System
knowledgebase/05-animation-system.md
Global animation creation, reverse animations, animation-driven logic, state-based control.
Key Patterns:
- •Create animations once in Preloader, use everywhere
- •Animation events for gameplay synchronization
- •State-based animation control with caching
- •External animation JSON data
6. Input Handling
knowledgebase/06-input-handling.md
Keyboard, pointer, touch, JustDown pattern, dual input support, input gating, cleanup.
Key Patterns:
- •JustDown for single-press detection
- •Dual keyboard + pointer support
- •Input gating during animations
- •Event-driven pointer movement
- •One-time input with
once()
7. State Management
knowledgebase/07-state-management.md
Scene-level state, registry for persistence, localStorage, GameObject data component, state machines.
Key Patterns:
- •Constructor for config, init() for resettable state
- •Registry for cross-scene data
- •GameObject data component for custom properties
- •Boolean flags vs state machines
8. Object Pooling & Memory
knowledgebase/08-object-pooling-memory.md
Group-based pooling, enable/disable pattern, object reuse, dynamic texture cleanup.
Key Patterns:
- •Pre-create with
frameQuantity, recycle withgetFirstDead() - •Enable/disable instead of create/destroy
- •Reset pattern for reusable objects
- •Dynamic texture cleanup prevention
9. Grid Systems
knowledgebase/09-grid-systems.md
Dual coordinates, 2D arrays, adjacency helpers, grid alignment, visualization.
Key Patterns:
- •Separate grid coordinates from pixel coordinates
- •2D array with bounds checking
- •Adjacency helpers (8-direction, 4-direction)
- •Grid-aligned group creation
10. Custom Game Objects
knowledgebase/10-custom-game-objects.md
Extending Sprite/Image, composition pattern, extending groups, self-registration.
Key Patterns:
- •Extend Sprite when physics + animations needed
- •Extend Image for static sprites with physics
- •Composition for non-visual managers
- •Must call
super.preUpdate()for animations
11. UI/HUD Patterns
knowledgebase/11-ui-hud-patterns.md
Text styling, positioning, dynamic updates, bitmap fonts, icon counters, layering.
Key Patterns:
- •Text origin for positioning (top-left, center, etc.)
- •Animated counters with tweens
- •Icon-based counters (hearts, lives)
- •Depth/z-ordering for layered UI
12. Tween & Visual Effects
knowledgebase/12-tween-visual-effects.md
Basic tweens, staggered animations, choreographed sequences, camera effects, particles.
Key Patterns:
- •Tween callbacks for sequencing
- •Staggered grid animations
- •Choreographed sequences with accumulating delays
- •Camera shake, flash, fade
- •Particle emitters
13. Audio Integration
knowledgebase/13-audio-integration.md
Sound management, audio lock handling, centralized audio, delayed/sequenced audio.
Key Patterns:
- •Multi-format audio loading
- •Audio lock detection and handling
- •Centralized audio management in UI scene
- •Persistent music across scenes
14. Game Loop Patterns
knowledgebase/14-game-loop-patterns.md
Standard update, minimal event-driven, preUpdate for custom objects, delegation, timers.
Key Patterns:
- •Early exit guards in update()
- •Event-driven for turn-based games
- •Timer-based recurring/one-shot events
- •Update delegation to managers
15. Algorithm Implementations
knowledgebase/15-algorithm-implementations.md
Flood fill, smart random placement, solvable puzzle generation, proximity calculation, following chains, AI.
Key Patterns:
- •Recursive flood fill with 4-direction spread
- •Valid position collection for guaranteed placement
- •Walker algorithm for solvable puzzles
- •Following chain with ShiftPosition
16. Performance Optimization
knowledgebase/16-performance-optimization.md
Object pooling, texture caching, minimize update logic, event-driven, static groups.
Key Patterns:
- •Object pooling eliminates GC pauses
- •Static groups for immovable objects
- •Event-driven over polling in update()
- •Texture atlas for rendering performance
17. Code Organization
knowledgebase/17-code-organization.md
File structure, ES6 modules, constants management, separation of concerns.
Key Patterns:
- •Scenes, gameobjects, managers, utils, constants folders
- •ES6 module imports
- •Constant files for sprite/animation/audio keys
- •Clear initialization order in create()
18. Development Philosophy
knowledgebase/18-development-philosophy.md
Architecture principles, when to use physics, scene decisions, update vs events, progressive difficulty.
Key Patterns:
- •When to use physics vs manual logic
- •Single vs multi vs parallel scenes
- •Update() vs event-driven approaches
- •Progressive difficulty patterns
Core Patterns Reference
Scene Setup
export default class Game extends Phaser.Scene {
constructor() {
super('Game');
// Configuration constants
this.GRID_SIZE = 32;
}
init() {
// Reset resettable state
this.score = 0;
this.gameOver = false;
}
create() {
// Initialize objects
this.player = new Player(this);
this.enemies = this.physics.add.group();
// Setup collisions
this.physics.add.collider(this.player, this.enemies, this.onHit, null, this);
}
update(time, delta) {
if (this.gameOver) return;
// Game logic
}
}
Custom Game Object
export default class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'player');
// Self-register
scene.add.existing(this);
scene.physics.add.existing(this);
// Configuration
this.setCollideWorldBounds(true);
this.isAlive = true;
}
preUpdate(time, delta) {
super.preUpdate(time, delta); // ESSENTIAL for animations
if (!this.isAlive) return;
this.handleMovement();
}
}
Grid System
// Dual coordinate system
this.gridX = 5;
this.gridY = 3;
this.sprite.x = this.gridX * TILE_SIZE;
this.sprite.y = this.gridY * TILE_SIZE;
// 2D array with bounds checking
getCellXY(x, y) {
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
return null;
}
return this.grid[y][x];
}
Collision Handling
// Physical collision
this.physics.add.collider(player, platforms);
// Trigger-based overlap
this.physics.add.overlap(player, coins, this.collectCoin, null, this);
// Collision with state gates
onHit(player, enemy) {
if (!player.isAlive) return;
if (player.invincible) return;
if (enemy.alpha < 1) return;
this.handleDamage(player, enemy);
}
Input Patterns
create() {
// Cursor keys
this.cursors = this.input.keyboard.createCursorKeys();
// Specific key
this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
// Pointer/touch
this.input.on('pointerdown', this.handleClick, this);
}
update() {
// Continuous input
if (this.cursors.left.isDown) {
this.player.setVelocityX(-160);
}
// Single-press input
if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
this.jump();
}
}
Animation Setup
// In Preloader create() - global animations
this.anims.create({
key: 'player-walk',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
// In Game create() - use animations
this.player.play('player-walk');
// Animation events
this.player.on('animationcomplete-attack', this.spawnProjectile, this);
State Management
// Scene-level state
init() {
this.score = 0;
this.lives = 3;
this.gameOver = false;
}
// Registry for cross-scene data
this.registry.set('highscore', this.score);
const highscore = this.registry.get('highscore');
// GameObject data
sprite.setData({ gridX: 5, gridY: 3, type: 'enemy' });
const gridX = sprite.data.values.gridX;
Common Pitfalls
Critical mistakes to avoid:
- •
Forgetting
super.preUpdate(time, delta)in custom sprites- •Animations won't work without this
- •Always call first thing in preUpdate()
- •
Not calling
refreshBody()after scaling static groupsjavascriptthis.platforms.create(x, y, 'platform') .setScale(2) .refreshBody(); // REQUIRED - •
Creating objects in update() instead of pooling
- •Causes GC pauses and poor performance
- •Pre-create pools, recycle with enable/disable
- •
Missing bounds checks on grid access
javascriptif (x < 0 || x >= width || y < 0 || y >= height) return null;
- •
Not cleaning up listeners/timers
javascriptshutdown() { this.input.off('pointerdown', this.handleClick); if (this.timer) this.timer.remove(); } - •
Using wrong collision method
- •Use
colliderfor physical blocking - •Use
overlapfor triggers/pickups
- •Use
- •
Polling in update() when events would work
- •Use physics overlap instead of distance checks
- •Use pointer events instead of checking every frame
- •
Hard-coding values instead of constants
javascript// Bad: magic numbers sprite.x = 400; // Good: named constants const GRID_SIZE = 32; sprite.x = gridX * GRID_SIZE;
- •
Not resetting state in init()
- •Scene restart won't work properly
- •Put all resettable state in init(), not constructor
- •
Ignoring context binding in callbacks
javascript// Use arrow functions or bind this.time.delayedCall(1000, () => this.start(), [], this);
Performance Tips
- •Object pools for frequently created/destroyed objects
- •Texture atlases combine sprites into single image
- •Static groups for immovable platforms/walls
- •Event-driven over polling in update()
- •Minimal update logic - only what's necessary each frame
- •Cache lookups - don't search arrays every frame
- •Set depth once - don't sort every frame
When to Read Detailed Files
Starting a new game? → Read 01-scene-architecture.md
Performance issues? → Read 16-performance-optimization.md
Grid-based game? → Read 09-grid-systems.md
Complex animations? → Read 05-animation-system.md and 12-tween-visual-effects.md
Input problems? → Read 06-input-handling.md
Memory leaks? → Read 08-object-pooling-memory.md
Implementing algorithms? → Read 15-algorithm-implementations.md
Architecture decisions? → Read 18-development-philosophy.md
Quick Reference Cheat Sheet
Scene Flow
Boot → Preloader → MainMenu → Game → GameOver
this.scene.start('NextScene', { score: this.score });
this.scene.launch('GameUI'); // Parallel scene
Physics
this.physics.add.collider(a, b); // Physical blocking this.physics.add.overlap(a, b, callback); // Trigger only sprite.setCollideWorldBounds(true); sprite.setImmovable(true);
Groups & Pooling
this.items = this.physics.add.group({ frameQuantity: 20 });
const item = this.items.getFirstDead(false);
item.enableBody(true, x, y, true, true);
item.disableBody(true, true);
Input
this.cursors = this.input.keyboard.createCursorKeys();
Phaser.Input.Keyboard.JustDown(key)
this.input.on('pointerdown', callback, this);
Tweens
this.tweens.add({
targets: sprite,
x: 400,
duration: 1000,
ease: 'Power2',
onComplete: () => {}
});
Camera
this.cameras.main.shake(duration, intensity); this.cameras.main.fadeOut(duration); this.cameras.main.flash(duration, r, g, b);
Test Seam Setup
Always include test seam in scene create() method
Expose game state, scene key, and common commands:
create() {
// ... scene initialization ...
// Test seam setup
window.__TEST__ = {
sceneKey: this.scene.key,
gameState: () => this.gameState,
commands: {
// Scene-specific commands
clickStartGame: () => {
this.scene.start('GameScene');
}
}
};
}
Document test seam in code comments for future reference.
Scene Navigation
Use this.scene.start('SceneName', data) for transitions
Pass game state via data parameter:
// Transition with data
this.scene.start('GameOverScene', {
score: this.gameState.score,
level: this.gameState.level
});
// In destination scene
create(data?: any) {
const score = data?.score || 0;
const level = data?.level || 1;
// Use passed data
}
Clean up resources in scene shutdown() method. Document navigation flow in progress.txt.
HUD Implementation (Enhanced)
Use Graphics objects for backgrounds (rounded rectangles):
// HUD background const bg = this.add.graphics(); bg.fillStyle(0x000000, 0.7); bg.fillRoundedRect(10, 10, 200, 50, 5); bg.setDepth(100); // Above game elements
Set depth to 100+ for HUD elements:
- •Background: depth 100
- •Text: depth 101+
Position relative to camera dimensions:
const x = this.cameras.main.width - 220; // Right side const y = 10; // Top
Update text in real-time via setText():
this.timerText.setText('01:00');
Audio Integration (Enhanced)
Load audio in scene preload() method:
preload() {
this.load.audio('bgMusic', 'assets/music.mp3');
}
Initialize AudioManager in create() method:
create() {
this.bgMusic = this.sound.add('bgMusic', { loop: true });
}
Respect volume settings from localStorage:
const settings = loadSettings(); this.bgMusic.setVolume(settings.musicVolume);
Use test seam to verify audio state:
window.__TEST__ = {
commands: {
getAudioState: () => ({
musicPlaying: this.bgMusic.isPlaying,
volume: this.bgMusic.volume
})
}
};
This skill provides comprehensive Phaser 3 patterns. Read the detailed knowledgebase files for in-depth examples and advanced techniques.
Camera Management Patterns
Camera zoom and centerOn() for full visibility:
// Camera zoom for full visibility this.cameras.main.setZoom(1.0); this.cameras.main.centerOn(canvasWidth / 2, canvasHeight / 2);
Viewport scaling with aspect ratio maintenance:
// Viewport scaling const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const gameWidth = 800; const gameHeight = 600; // Maintain aspect ratio const scaleX = viewportWidth / gameWidth; const scaleY = viewportHeight / gameHeight; const scale = Math.min(scaleX, scaleY); this.cameras.main.setZoom(scale);
window.innerWidth/innerHeight for viewport calculations:
// Use window dimensions for viewport const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // Calculate camera bounds this.cameras.main.setBounds(0, 0, gameWidth, gameHeight); this.cameras.main.setScroll(0, 0);
Canvas centering with flexbox:
/* CSS for canvas centering */
canvas {
display: block;
margin: 0 auto;
}
Audio Integration Patterns
Loading audio in preload() method:
preload() {
// Load audio files
this.load.audio('bgMusic', 'assets/audio/music/bg-music.mp3');
this.load.audio('coinCollect', 'assets/audio/sfx/coin-collect.mp3');
}
Volume control integration with Settings:
create() {
// Get volume settings
const settings = loadSettings();
// Set background music volume
this.bgMusic = this.sound.add('bgMusic', { loop: true });
this.bgMusic.setVolume(settings.musicVolume);
// Set sound effects volume
this.coinSound = this.sound.add('coinCollect');
this.coinSound.setVolume(settings.sfxVolume);
}
Background music with looping:
create() {
this.bgMusic = this.sound.add('bgMusic', { loop: true });
this.bgMusic.play();
}
Sound effect triggering patterns:
collectCoin() {
// Play sound effect
this.coinSound.play();
// Update score
this.gameState.score += 10;
}
Test seam commands for audio testing:
window.__TEST__.commands = {
getAudioState: () => ({
musicPlaying: this.bgMusic?.isPlaying || false,
musicVolume: this.bgMusic?.volume || 0,
sfxVolume: this.coinSound?.volume || 0
}),
setMusicVolume: (volume: number) => {
this.bgMusic?.setVolume(volume);
}
};
Sprite Management Patterns
Individual sprite images vs spritesheets:
// Individual sprites
this.load.image('wizard-south', 'assets/wizard-south.png');
this.load.image('wizard-north', 'assets/wizard-north.png');
// Spritesheet
this.load.spritesheet('wizard', 'assets/wizard-sheet.png', {
frameWidth: 48,
frameHeight: 48
});
Proper scaling (setScale) and origin (setOrigin):
// Set scale sprite.setScale(2.0); // 2x size // Set origin (0.5 = center, 0 = top-left) sprite.setOrigin(0.5, 0.5); // Center origin
Texture existence checks (this.textures.exists()):
// Check if texture exists before using
if (this.textures.exists('wizard-south')) {
this.wizard = this.add.sprite(100, 100, 'wizard-south');
} else {
// Fallback
this.wizard = this.add.rectangle(100, 100, 48, 48, 0x0000ff);
console.warn('Wizard texture not found, using placeholder');
}
Fallback to placeholder graphics:
create() {
if (this.textures.exists('wizard-south')) {
this.wizard = this.add.sprite(100, 100, 'wizard-south');
} else {
// Fallback: Create placeholder
this.wizard = this.add.rectangle(100, 100, 48, 48, 0x0000ff);
this.wizard.setStrokeStyle(2, 0x000000);
console.warn('Using placeholder for wizard sprite');
}
}
Union types for Sprite | Rectangle:
// Union type for flexible objects
type SpriteOrRectangle = Phaser.GameObjects.Sprite | Phaser.GameObjects.Rectangle;
function handleObject(obj: SpriteOrRectangle) {
if (obj instanceof Phaser.GameObjects.Sprite) {
// Sprite-specific code
} else {
// Rectangle-specific code
}
}
Error Handling Patterns
Retry logic with new seeds:
generateMaze(maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const maze = this.mazeGenerator.generate(this.seed + i);
return maze;
} catch (error) {
console.warn(`Maze generation attempt ${i + 1} failed:`, error);
if (i === maxRetries - 1) {
// Use fallback maze
return this.createFallbackMaze();
}
}
}
}
Fallback mechanisms for critical operations:
create() {
try {
// Try to load generated asset
this.load.image('wizard', generatedAssetUrl);
} catch (error) {
// Fallback to placeholder
console.warn('Failed to load generated asset, using placeholder');
this.load.image('wizard', 'assets/fallback/wizard.png');
}
}
Try-catch in scene create() methods:
create() {
try {
// Scene initialization
this.initScene();
} catch (error) {
console.error('Scene initialization failed:', error);
// Graceful degradation
this.createFallbackScene();
}
}
Console warnings for debugging:
if (!this.textures.exists('asset-key')) {
console.warn('Asset not found:', 'asset-key');
console.warn('Using fallback placeholder');
}
Test seam commands for error scenarios:
window.__TEST__.commands = {
forceMazeFailure: () => {
this.mazeGenerator.forceFailure = true;
},
restoreMazeGeneration: () => {
this.mazeGenerator.forceFailure = false;
}
};