| name | dev-phaser-sprite-management |
| description | Sprites, sprite sheets, texture atlases, and object pooling for Phaser |
Phaser Sprite Management
"Efficient sprite handling – sheets, atlases, and object pools."
Before/After: Manual Arrays vs Phaser Object Pooling
❌ Before: Manual Array Management
interface Bullet {
x: number;
y: number;
vx: number;
vy: number;
active: boolean;
}
const bullets: Bullet[] = [];
let lastShotTime = 0;
function shoot(x: number, y: number, dx: number, dy: number) {
const bullet: Bullet = {
x, y,
vx: dx * 500,
vy: dy * 500,
active: true
};
bullets.push(bullet);
}
function updateBullets(dt: number) {
for (let i = bullets.length - 1; i >= 0; i--) {
const b = bullets[i];
b.x += b.vx * dt;
b.y += b.vy * dt;
if (b.x < 0 || b.x > 800 || b.y < 0 || b.y > 600 || !b.active) {
bullets.splice(i, 1);
}
}
}
✅ After: Phaser Object Pooling
export class GameScene extends Phaser.Scene {
private bulletPool!: Phaser.GameObjects.Group;
create() {
this.bulletPool = this.add.group({
defaultKey: 'bullet',
maxSize: 50,
createCallback: (bullet: Phaser.GameObjects.Image) => {
bullet.setActive(false).setVisible(false);
}
});
for (let i = 0; i < 20; i++) {
this.bulletPool.get(0, 0);
}
}
fireBullet(x: number, y: number, vx: number, : ) {
bullet = ..(x, y) ..;
(bullet) {
bullet.().();
..({
: bullet,
: x + vx * ,
: y + vy * ,
: ,
: {
..(bullet);
}
});
}
}
}
When to Use This Skill
Use when:
- Creating and managing game sprites
- Working with sprite sheets and texture atlases
- Implementing object pooling for performance
- Optimizing sprite rendering
- Managing sprite animations
Quick Start
this.load.spritesheet("player", "assets/player.png", {
frameWidth: 32,
frameHeight: 32,
startFrame: 0,
endFrame: 15,
});
const player = this.add.sprite(400, 300, "player", 0);
player.play("idle");
Decision Framework
| Need | Use |
|---|
| Single image | load.image() + add.image() |
| Animation frames | load.spritesheet() |
| Multiple sprites | load.atlas() |
| Many reusable objects | Object pool |
| Physics body | physics.add.sprite() |
Progressive Guide
Level 1: Basic Sprite Loading
export class MainScene extends Phaser.Scene {
preload() {
this.load.image("player", "assets/player.png");
this.load.spritesheet("coin", "assets/coin.png", {
frameWidth: 16,
frameHeight: 16,
endFrame: 7,
});
this.load.spritesheet("player", "assets/player.png", {
frameWidth: 32,
frameHeight: 48,
startFrame: 0,
endFrame: 47,
});
}
create() {
const player = this.add.sprite(400, 300, "player");
player.setFrame();
player.();
player.();
}
}
Level 2: Texture Atlas Loading
preload() {
this.load.atlas('game', 'assets/atlas.png', 'assets/atlas.json');
this.load.atlasXML('ui', 'assets/ui.png', 'assets/ui.xml');
this.load.atlas('items', 'assets/items.png', 'assets/items.json');
}
create() {
const sword = this.add.image(400, 300, 'game', 'sword.png');
const shield = this.add.image(400, 350, 'game', 'shield.png');
}
Level 3: Sprite Animations
create() {
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('player', {
start: 0,
end: 3
}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', {
start: 4,
end: 11
}),
frameRate: 12,
repeat: -1
});
this.anims.create({
key: 'attack',
frames: this.anims.generateFrameNumbers('player', {
start: 12,
end:
}),
: ,
:
});
player = ..(, , );
player.();
player.(, );
}
Level 4: Object Pooling
export class MainScene extends Phaser.Scene {
private bulletPool!: Phaser.GameObjects.Group;
private readonly MAX_BULLETS = 50;
create() {
this.bulletPool = this.add.group({
defaultKey: "bullet",
maxSize: this.MAX_BULLETS,
createCallback: (bullet: Phaser.GameObjects.Image) => {
bullet.setActive(false).setVisible(false);
},
});
for (let i = 0; i < 20; i++) {
this.bulletPool.get(0, 0);
}
this.input.on("pointerdown", this., );
}
() {
bullet = ..(
..,
..,
) ..;
(bullet) {
bullet.().();
..({
: bullet,
: bullet. + ,
: ,
: {
bullet.().();
..(bullet);
},
});
}
}
}
Level 5: Advanced Sprite Management
export class MainScene extends Phaser.Scene {
private spriteManager!: SpriteManager;
create() {
this.spriteManager = new SpriteManager(this);
this.spriteManager.registerType("enemy", {
poolSize: 30,
onCreate: (sprite) => this.setupEnemy(sprite),
onActivate: (sprite, data) => this.spawnEnemy(sprite, data),
onDeactivate: (sprite) => this.cleanupEnemy(sprite),
});
}
update() {
this.spriteManager.spawn("enemy", { x: 400, y: 100, type: "flying" });
}
}
class {
pools = <, ..>();
() {}
() {
..(
,
...({
: ,
: config.,
: config.,
: config.,
}),
);
}
() {
pool = ..();
(!pool) ;
sprite = pool.(data., data.);
(sprite) {
sprite.().();
pool..(sprite, data);
}
sprite;
}
() {
pool = ..();
(pool) {
pool.(sprite);
}
}
}
Anti-Patterns
❌ DON'T:
- Load individual images for sprite frames - use sprite sheets
- Create/destroy sprites every frame - use object pooling
- Use large atlases without grouping - organize by scene/use
- Forget to
killAndHide() pooled objects before reuse
- Mix pixel densities in sprite sheets
- Use
add.sprite() when physics needed - use physics.add.sprite()
✅ DO:
- Use TexturePacker or similar for atlas generation
- Pre-allocate pools during scene creation
- Group atlas textures by logical usage
- Set active/visible false when returning to pool
- Use consistent frame sizes in sprite sheets
- Cache frequently used sprites
Code Patterns
Object Pool with Custom Class
class Bullet extends Phaser.GameObjects.Image {
declare body: Phaser.Physics.Arcade.Body;
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, 'bullet');
scene.add.existing(this);
scene.physics.add.existing(this);
}
fire(x: number, y: number, velocity: Phaser.Math.Vector2) {
this.setPosition(x, y);
this.body.setVelocity(velocity.x, velocity.y);
this.setActive(true);
this.setVisible(true);
}
reset() {
..(, );
.();
.();
.(-, -);
}
}
() {
. = ..({
: ,
: ,
:
});
}
Sprite Sheet with Multi-Row Animation
this.anims.create({
key: "idle",
frames: this.anims.generateFrameNumbers("player", {
start: 0,
end: 3,
}),
frameRate: 8,
repeat: -1,
yoyo: false,
});
this.anims.create({
key: "walk",
frames: this.anims.generateFrameNumbers("player", {
start: 4,
end: 7,
}),
frameRate: 12,
repeat: -1,
});
Checklist
Reference