- name
- animations
- description
- Use this skill when creating or controlling sprite animations in Phaser 4. Covers spritesheets, atlases, AnimationManager, AnimationState, play/stop/chain, frame callbacks, and animation events. Triggers on: sprite animation, spritesheet, play animation, animation frames.
# Phaser 4 -- Sprite Animations
> AnimationManager (global), AnimationState (per-sprite), creating animations from spritesheets and atlases, playing/pausing/chaining, animation events, frame callbacks.
**Related skills:** ../sprites-and-images/SKILL.md, ../loading-assets/SKILL.md
---
## Quick Start
```js
// In preload -- load a spritesheet
this.load.spritesheet('explosion', 'explosion.png', {
frameWidth: 64,
frameHeight: 64
});
// In create -- define a global animation
this.anims.create({
key: 'explode',
frames: this.anims.generateFrameNumbers('explosion', { start: 0, end: 11 }),
frameRate: 24,
repeat: 0
});
// Play it on a sprite
const sprite = this.add.sprite(400, 300, 'explosion');
sprite.play('explode');
```
---
## Core Concepts
### AnimationManager vs AnimationState
Phaser has two distinct animation objects:
| Aspect | AnimationManager | AnimationState |
|---|---|---|
| Access | `this.anims` (in a Scene) or `this.game.anims` | `sprite.anims` |
| Scope | Global -- shared across all scenes | Per-sprite instance |
| Purpose | Create/store animation definitions | Control playback on one Game Object |
| Class | `Phaser.Animations.AnimationManager` | `Phaser.Animations.AnimationState` |
The AnimationManager is a singleton owned by the Game. Animations registered there are available in every Scene. The AnimationState lives on each Sprite and handles playback for that specific object.
An `Animation` is a sequence of `AnimationFrame` objects plus timing data. Created via `this.anims.create(config)` (global) or `sprite.anims.create(config)` (local to one sprite).
### Local vs Global Animations
When `sprite.anims.play(key)` is called, it first checks for a local animation with that key, then falls back to the global AnimationManager. Use local for sprite-specific animations; use global when shared across sprites.
```js
// Global animation -- available to all sprites
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
// Local animation -- only on this sprite
sprite.anims.create({ key: 'walk', frames: 'npc_walk', frameRate: 10, repeat: -1 });
// This plays the LOCAL version because local takes priority
sprite.play('walk');
```
---
## Common Patterns
### Spritesheet Animation
Use `generateFrameNumbers` for spritesheets (numeric frame indices).
```js
this.load.spritesheet('dude', 'dude.png', { frameWidth: 32, frameHeight: 48 });
// All frames
this.anims.create({
key: 'run',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1
});
// Custom frame sequence
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('dude', { frames: [0, 1, 2, 1] }),
frameRate: 6,
repeat: -1
});
```
`generateFrameNumbers` config:
- `start` (default `0`) -- first frame index
- `end` (default `-1`, meaning last frame) -- final frame index
- `first` -- a single frame to prepend before the range
- `frames` -- explicit array of frame indices (overrides start/end)
### Atlas Animation
Use `generateFrameNames` for texture atlases (string-based frame names).
```js
this.load.atlas('gems', 'gems.png', 'gems.json');
this.anims.create({
key: 'ruby_sparkle',
frames: this.anims.generateFrameNames('gems', {
prefix: 'ruby_',
start: 1,
end: 6,
zeroPad: 4 // produces ruby_0001 through ruby_0006
}),
frameRate: 12,
repeat: -1
});
```
`generateFrameNames` config:
- `prefix` -- prepended to each frame number
- `suffix` -- appended after each frame number
- `start`, `end` -- numeric range
- `zeroPad` -- left-pad numbers to this length with zeros
- `frames` -- explicit array of frame numbers (overrides start/end)
If you call `generateFrameNames(key)` with no config, it returns all frames from the atlas.
### String as Frames
Pass a texture key string as `frames` to use all frames from that texture, sorted numerically by default. Set `sortFrames: false` to disable sorting.
```js
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
```
### Yoyo and Repeat
```js
this.anims.create({
key: 'pulse',
frames: this.anims.generateFrameNumbers('orb', { start: 0, end: 5 }),
frameRate: 10,
yoyo: true, // plays forward then backward
repeat: -1, // -1 = forever
repeatDelay: 500 // ms pause between each repeat cycle
});
```
When `yoyo` is true, the animation plays forward then reverses. The full cycle counts as one play.
### Chaining Animations
```js
sprite.play('attack');
sprite.chain('idle'); // play idle after attack completes
sprite.chain(['fall', 'land', 'idle']); // chain multiple
sprite.anims.chain(); // clear the chain queue
```
Chaining is per-sprite. Chained animations start after `animationcomplete` or `animationstop`. An animation with `repeat: -1` never completes -- call `stop()` to trigger the chain.
### Playing in Reverse
```js
// Play an animation from last frame to first
sprite.playReverse('walk');
// Reverse direction mid-playback
sprite.anims.reverse();
```
`playReverse` sets `forward = false` and `inReverse = true`. The `reverse()` method toggles direction mid-playback.
### Play Variants
```js
sprite.play('walk', true); // ignoreIfPlaying = true
sprite.anims.playAfterDelay('walk', 1000); // play after 1s delay
sprite.anims.playAfterRepeat('walk', 2); // play after current anim repeats 2x
```
### Animation Mixing
Adds a transition delay between two specific animations, set globally on the AnimationManager.
```js
this.anims.addMix('idle', 'walk', 200);
this.anims.addMix('walk', 'idle', 300);
sprite.play('idle');
sprite.play('walk'); // 200ms mix delay applied automatically
this.anims.removeMix('idle', 'walk'); // remove specific pair
this.anims.removeMix('idle'); // remove all mixes for 'idle'
```
Mix delays only apply with `sprite.play()`, not `playAfterDelay` or `playAfterRepeat`.
### Pause, Resume, and Stop
```js
sprite.anims.pause(); // pause per-sprite
sprite.anims.resume(); // resume per-sprite
this.anims.pauseAll(); // global pause
this.anims.resumeAll(); // global resume
sprite.anims.stop(); // stop immediately
sprite.anims.stopAfterDelay(2000); // stop after 2 seconds
sprite.anims.stopAfterRepeat(1); // stop after 1 more repeat
sprite.anims.stopOnFrame(frame); // stop when a specific frame is reached
```
All stop methods fire `animationstop` (not `animationcomplete`). Chained animations trigger after stop.
### Animation Events
```js
sprite.on('animationcomplete', (anim, frame, gameObject, frameKey) => {
console.log('completed:', anim.key);
});
// Key-specific complete -- only fires for the named animation
sprite.on('animationcomplete-explode', (anim, frame, gameObject, frameKey) => {
gameObject.destroy();
});
```
Available events: `animationstart`, `animationcomplete`, `animationcomplete-{key}`, `animationupdate`, `animationstop`, `animationrepeat`, `animationrestart`. All share the same callback signature: `(animation, frame, gameObject, frameKey)`.
### Frame-Level Callbacks via animationupdate
```js
sprite.on('animationupdate', (anim, frame, gameObject, frameKey) => {
if (anim.key === 'attack' && frame.index === 4) {
this.checkHit(gameObject);
}
});
```
### Per-Frame Duration
Individual frames can have a `duration` (ms) that is added to the base msPerFrame.
```js
this.anims.create({
key: 'combo',
frames: [
{ key: 'fighter', frame: 'punch1', duration: 50 },
{ key: 'fighter', frame: 'kick', duration: 200 }, // hold longer
{ key: 'fighter', frame: 'recover', duration: 100 }
],
frameRate: 24
});
```
### Visibility, Random Start, and TimeScale
```js
// Visibility control
this.anims.create({
key: 'appear', frames: 'sparkle', frameRate: 12,
showOnStart: true, // sprite.visible = true when anim starts (after delay)
hideOnComplete: true, // sprite.visible = false when anim completes
showBeforeDelay: true // show first frame immediately even during delay
});
// Random start frame -- each sprite begins on a different frame
this.anims.create({
key: 'ambient', frames: 'fire', frameRate: 10, repeat: -1,
randomFrame: true
});
// TimeScale -- per-sprite or global speed control
sprite.anims.timeScale = 2; // 2x speed
sprite.play({ key: 'walk', timeScale: 0.5 }); // half speed via config
this.anims.globalTimeScale = 0.5; // affects ALL animations
```
### Staggered Playback
```js
const enemies = this.add.group({ key: 'enemy', repeat: 9 });
this.anims.staggerPlay('walk', enemies.getChildren(), 100);
// Each sprite starts 100ms after the previous. Pass staggerFirst: false to skip delay on first.
```
### JSON Export and Import
```js
// Export all global animations to JSON
const data = this.anims.toJSON();
// Import animations from JSON (pass true to clear existing animations first)
this.anims.fromJSON(data);
this.anims.fromJSON(data, true);
// Check before creating to avoid duplicate warning
if (!this.anims.exists('walk')) {
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
}
```
### Modifying Animation Frames at Runtime
```js
const anim = this.anims.get('walk');
// Add frames to the end
anim.addFrame(this.anims.generateFrameNumbers('player', { start: 8, end: 10 }));
// Insert frames at a specific index
anim.addFrameAt(this.anims.generateFrameNumbers('player', { frames: [5] }), 2);
// Remove a specific frame object
const frame = anim.frames[3];
anim.removeFrame(frame);
// Remove frame at index
anim.removeFrameAt(0);
```
### Aseprite Support
```js
this.load.aseprite('paladin', 'paladin.png', 'paladin.json');
// In create:
this.anims.createFromAseprite('paladin'); // all tags
this.anims.createFromAseprite('paladin', ['walk']); // specific tags only
sprite.play('walk'); // play by tag name
```
---
## Configuration Reference
### AnimationConfig (used with `this.anims.create()`)
| Property | Type | Default | Description |
|---|---|---|---|
| `key` | string | -- | Unique identifier for the animation |
| `frames` | string or AnimationFrame[] | `[]` | Texture key string (uses all frames) or array of frame config objects |
| `sortFrames` | boolean | `true` | Numerically sort frames when using a string key |
| `defaultTextureKey` | string | `null` | Fallback texture key if not set per-frame |
| `frameRate` | number | `24` | Playback rate in frames per second (used if `duration` is null) |
| `duration` | number | `null` | Total animation length in ms (derives frameRate if set) |
| `skipMissedFrames` | boolean | `true` | Skip frames when lagging behind |
| `delay` | number | `0` | Delay before playback starts (ms) |
| `repeat` | number | `0` | Times to repeat after first play (-1 = infinite) |
| `repeatDelay` | number | `0` | Delay before each repeat (ms) |
| `yoyo` | boolean | `false` | Reverse back to start before repeating |
| `showBeforeDelay` | boolean | `false` | Show first frame immediately during delay period |
GitHub에서 보기