Sprites & Sheets
A sprite is a 2D image rendered into a scene, often animated by swapping frames or by playing back a sub-region of a sprite sheet. Engines wrap this with pivot, scale, tint, flipping, and blending controls.
Sheet animation in Phaser / Pixi
EXAMPLE
// 1) Phaser 3 — sprite from an image
this.load.image('player', 'assets/player.png');
// in create()
const p = this.add.sprite(200, 300, 'player');
p.setOrigin(0.5, 1); // pivot at bottom-center (feet on ground)
p.setScale(2); // pixel-art style: integer scale
p.setFlipX(true); // face left
p.setTint(0xff8888); // damage flash
// 2) Sprite sheet — uniform grid of frames
this.load.spritesheet('hero', 'assets/hero.png', {
frameWidth: 32,
frameHeight: 48,
margin: 0,
spacing: 0,
});
// Animation = sequence of frame indices
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('hero', { start: 0, end: 7 }),
frameRate: 12,
repeat: -1, // loop forever
});
this.anims.create({
key: 'jump',
frames: this.anims.generateFrameNumbers('hero', { frames: [8, 9, 10] }),
frameRate: 8,
repeat: 0,
});
const hero = this.add.sprite(100, 200, 'hero', 0);
hero.play('walk');
// 3) Atlas (TexturePacker) — non-uniform, named frames
this.load.atlas('fx', 'assets/fx.png', 'assets/fx.json');
this.anims.create({
key: 'explode',
frames: this.anims.generateFrameNames('fx', {
prefix: 'explosion_',
start: 0, end: 11,
zeroPad: 2,
suffix: '.png',
}),
frameRate: 24,
hideOnComplete: true,
});
this.add.sprite(x, y, 'fx').play('explode');
// 4) Physics body sized to art (don't use the whole frame)
this.physics.add.existing(hero);
hero.body.setSize(20, 40); // hitbox narrower than the 32-wide art
hero.body.setOffset(6, 8);
// 5) Pixi.js — sprites from a sheet
import { Application, Assets, AnimatedSprite, Spritesheet } from 'pixi.js';
const app = new Application();
await app.init({ background: '#222', resizeTo: window });
document.body.appendChild(app.canvas);
const sheetData = {
frames: {
'walk_0.png': { frame: { x: 0, y: 0, w: 32, h: 48 } },
'walk_1.png': { frame: { x: 32, y: 0, w: 32, h: 48 } },
'walk_2.png': { frame: { x: 64, y: 0, w: 32, h: 48 } },
'walk_3.png': { frame: { x: 96, y: 0, w: 32, h: 48 } },
},
meta: { scale: 1 },
};
const texture = await Assets.load('assets/hero.png');
const sheet = new Spritesheet(texture, sheetData);
await sheet.parse();
const hero2 = new AnimatedSprite(Object.values(sheet.textures));
hero2.animationSpeed = 0.2; // frames per tick
hero2.anchor.set(0.5, 1);
hero2.play();
app.stage.addChild(hero2);
// 6) Performance tips
// • Pack art into atlases — one texture, many draws → fewer batch breaks
// • Use the same blend mode in a group to keep the batcher hot
// • Disable culling explicitly for static UI; let the engine cull for the world
// • Pool sprites (especially for bullets, particles) instead of new/destroy
//
class BulletPool {
constructor(scene, key, size = 100) {
this.scene = scene;
this.group = scene.add.group({
classType: Phaser.GameObjects.Sprite,
defaultKey: key,
maxSize: size,
});
}
fire(x, y, vx, vy) {
const b = this.group.get(x, y);
if (!b) return;
b.setActive(true).setVisible(true);
this.scene.physics.add.existing(b);
b.body.setVelocity(vx, vy);
}
despawn(b) { b.setActive(false).setVisible(false); b.body.stop(); }
}
// 7) Pixel-perfect rendering
this.game.config.pixelArt = true; // turn off bilinear filtering
this.cameras.main.setZoom(3); // integer zoom
hero.setScale(1); // never fractional for pixel art
// 8) Tinting + flashing on hit
function flash(sprite) {
sprite.setTint(0xffffff);
sprite.scene.time.delayedCall(80, () => sprite.clearTint());
}
// 9) Layering with depth
background.setDepth(-10);
platform.setDepth(0);
hero.setDepth(10);
ui.setDepth(100);
// 10) Common bugs
// • Origin at (0.5, 0.5) but using setPosition for feet on ground → floats
// • Body size matches art size → hitbox too generous
// • One sprite per file → tons of HTTP requests + batch breaks
// • Animation key collisions between scenes → 'animation already exists'
// • Forgetting hideOnComplete on a one-shot effect → corpse left in scene
// • Fractional scale on pixel art → blurry mess
Why it matters
Pack sprites into an atlas, give each animation a dedicated key, and pool fast-spawned sprites like bullets and particles. The art looks the same either way, but draw calls and GC pressure are the difference between 60fps and a stuttery 22.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Sprite sheets pack frames in one image.
// Update animation by indexing into the sheet each tick.
function draw(ctx, sheet, frame, x, y) {
ctx.drawImage(sheet, frame * 32, 0, 32, 32, x, y, 32, 32);
}
Try it Yourself »
Discussion
Loading…