mcpField
A
animations

animations

github.com/phaserjs开发40.6k1.1k 次安装10 周前更新
代码仓库

关于 animations

面向 Phaser 4 开发者的 Sprite 动画技能参考,介绍动画定义与播放控制,涵盖精灵表、纹理图集、事件和逐帧回调。

主要功能

  1. 01全局与 Sprite 局部动画定义
  2. 02精灵表与图集帧生成
  3. 03播放、反向、暂停和串联控制
  4. 04动画事件与逐帧回调
  5. 05JSON 与 Aseprite 动画导入

适用场景

  1. 01创建循环角色动画
  2. 02串联攻击、坠落和着陆动画
  3. 03在特定帧触发游戏逻辑
  4. 04导入 Aseprite 动画标签

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phaserjs/phaser --skill animations

安装后在 Claude Code 中输入/animations确认已加载。

详细介绍

Phaser 4 -- 精灵动画

AnimationManager(全局)、AnimationState(每个精灵)、从 spritesheet 和 atlas 创建动画、播放/暂停/链接、动画事件、帧回调。

相关技能: ../sprites-and-images/SKILL.md、../loading-assets/SKILL.md


快速开始

// 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');

核心概念

AnimationManager 与 AnimationState

Phaser 有两个不同的动画对象:

方面AnimationManagerAnimationState
访问this.anims(在 Scene 中)或 this.game.animssprite.anims
范围全局——在所有场景间共享每个精灵实例
用途创建/存储动画定义控制单个 Game Object 上的播放
类Phaser.Animations.AnimationManagerPhaser.Animations.AnimationState

AnimationManager 是由 Game 拥有的单例。在那里注册的动画在每个 Scene 中都可用。AnimationState 存在于每个 Sprite 上,并处理该特定对象的播放。

一个 Animation 是一系列 AnimationFrame 对象加上时间数据。通过 this.anims.create(config)(全局)或 sprite.anims.create(config)(局部于一个精灵)创建。

局部动画与全局动画

当调用 sprite.anims.play(key) 时,它首先检查具有该键的局部动画,然后回退到全局 AnimationManager。对于特定于精灵的动画,请使用局部;当在多个精灵间共享时,请使用全局。

// 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');

常见模式

Spritesheet 动画

对 spritesheet(数字帧索引)使用 generateFrameNumbers。

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 配置:

  • start(默认 0)——第一帧索引
  • end(默认 -1,表示最后一帧)——最终帧索引
  • first——在范围之前前置的单个帧
  • frames——明确的帧索引数组(覆盖 start/end)
Atlas 动画

对纹理 atlas(基于字符串的帧名称)使用 generateFrameNames。

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 配置:

  • prefix——前置到每个帧编号
  • suffix——附加在每个帧编号之后
  • start、end——数字范围
  • zeroPad——用零将数字左填充到此长度
  • frames——明确的帧编号数组(覆盖 start/end)

如果在没有配置的情况下调用 generateFrameNames(key),它会返回 atlas 中的所有帧。

字符串作为帧

将纹理键字符串作为 frames 传入,以使用该纹理中的所有帧,默认按数字排序。设置 sortFrames: false 可禁用排序。

this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
Yoyo 与 Repeat
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
});

当 yoyo 为 true 时,动画先向前播放然后反向播放。整个循环算作一次播放。

链接动画
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

链接是按精灵的。链接的动画在 animationcomplete 或 animationstop 之后开始。具有 repeat: -1 的动画永远不会完成——调用 stop() 来触发链接。

反向播放
// Play an animation from last frame to first
sprite.playReverse('walk');

// Reverse direction mid-playback
sprite.anims.reverse();

playReverse 设置 forward = false 和 inReverse = true。reverse() 方法在播放中途切换方向。

播放变体
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
动画混合

在两个特定动画之间添加过渡延迟,全局设置在 AnimationManager 上。

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'

混合延迟仅适用于 sprite.play(),不适用于 playAfterDelay 或 playAfterRepeat。

暂停、恢复与停止
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

所有停止方法都会触发 animationstop(而不是 animationcomplete)。链接的动画在停止后触发。

动画事件
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();
});

可用事件:animationstart、animationcomplete、animationcomplete-{key}、animationupdate、animationstop、animationrepeat、animationrestart。它们都共享相同的回调签名:(animation, frame, gameObject, frameKey)。

通过 animationupdate 的帧级回调
sprite.on('animationupdate', (anim, frame, gameObject, frameKey) => {
    if (anim.key === 'attack' && frame.index === 4) {
        this.checkHit(gameObject);
    }
});
每帧持续时间

单独的帧可以有一个 duration(毫秒),它会被加到基础的 msPerFrame 上。

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
});
可见性、随机开始与 TimeScale
// 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
错开播放
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 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 });
}
运行时修改动画帧
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 支持
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

配置参考

AnimationConfig(与 this.anims.create() 一起使用)
属性类型默认值描述
keystring--动画的唯一标识符
framesstring 或 AnimationFrame[][]纹理键字符串(使用所有帧)或帧配置对象数组
sortFramesbooleantrue使用字符串键时对帧进行数值排序
defaultTextureKeystringnull未按帧设置时的回退纹理键
frameRatenumber24播放速率,单位为帧每秒(当 duration 为 null 时使用)
durationnumbernull动画总时长,单位为毫秒(设置后会推导出 frameRate)
skipMissedFramesbooleantrue落后时跳过帧
delaynumber0播放开始前的延迟(毫秒)
repeatnumber0首次播放后重复的次数(-1 = 无限)
repeatDelaynumber0每次重复前的延迟(毫秒)
yoyobooleanfalse重复前反向回到起始位置
showBeforeDelaybooleanfalse延迟期间立即显示第一帧
showOnStartbooleanfalse动画开始时设置 visible=true
hideOnCompletebooleanfalse动画完成时设置 visible=false
randomFramebooleanfalse从随机帧开始
PlayAnimationConfig(与 sprite.play() 一起使用)

所有 AnimationConfig 计时属性均可用,此外还有:

属性类型默认值描述
keystring 或 Animation--要播放的动画键或动画实例
startFramenumber0开始播放的帧索引
timeScalenumber1此播放的速度倍数

PlayAnimationConfig 中的值会覆盖动画定义,仅对此次特定播放生效。

Duration 与 FrameRate 的优先级
  • 如果 duration 和 frameRate 均为 null:默认为 24 fps。
  • 如果只设置了 duration:frameRate 计算为 totalFrames / (duration / 1000)。
  • 如果设置了 frameRate(即使同时设置了 duration):frameRate 优先,duration 推导为 (totalFrames / frameRate) * 1000。

事件

Sprite 事件流
  1. animationstart -- 延迟结束后、首次更新前
  2. animationupdate -- 每次帧变化时
  3. animationrepeat -- 每个重复周期
  4. animationcomplete -- 自然结束(有限重复)
  5. animationcomplete-{key} -- 同上,附带动画键

手动停止:触发 animationstop 而非 complete。播放中途重新开始:触发 animationrestart。

所有回调:(animation, frame, gameObject, frameKey)。

AnimationManager 事件

在 this.anims 上触发:addanimation、removeanimation、pauseall、resumeall。


API 快速参考

AnimationManager (this.anims)
方法说明
create(config)创建并注册一个全局动画
remove(key)按 key 移除一个全局动画
get(key)按 key 获取一个 Animation 实例
exists(key)检查某个 key 是否已注册
generateFrameNumbers(key, config)从精灵图集生成帧数组
generateFrameNames(key, config)从图集生成帧数组
play(key, children)在一组游戏对象上播放动画
staggerPlay(key, children, stagger)在多个游戏对象上交错播放
pauseAll() / resumeAll()全局暂停/恢复所有动画
addMix(animA, animB, delay)设置两个动画之间的过渡延迟
removeMix(animA, animB?)移除一组混合配对
getMix(animA, animB)获取两个动画之间的混合延迟
createFromAseprite(key, tags?, target?)从 Aseprite JSON 创建动画
toJSON()将所有动画导出为 JSON 数据
fromJSON(data, clear?)从 JSON 数据加载动画(传入 true 可先清除现有动画)
AnimationState (sprite.anims)
方法说明
play(key, ignoreIfPlaying?)播放一个动画
playReverse(key, ignoreIfPlaying?)反向播放一个动画
playAfterDelay(key, delay)延迟若干毫秒后播放
playAfterRepeat(key, repeatCount?)在当前动画重复 N 次后播放
chain(key)将动画排队在当前动画之后播放
stop()立即停止
stopAfterDelay(delay)延迟若干毫秒后停止
stopAfterRepeat(repeatCount?)再重复 N 次后停止
stopOnFrame(frame)到达特定帧时停止
pause(atFrame?)暂停播放
resume(fromFrame?)恢复播放
restart(includeDelay?, resetRepeats?)从头重新开始
reverse()在播放中途反向
getName()获取当前动画的 key
getFrameName()获取当前帧的 key
getProgress()获取进度 0-1
setProgress(value)设置进度 0-1
setRepeat(value)在播放期间更改重复次数
getTotalFrames()获取总帧数
create(config)在此精灵上创建一个本地动画
exists(key)检查某个本地动画是否存在
get(key)按 key 获取一个本地动画

关键属性:isPlaying、hasStarted、currentAnim、currentFrame、forward、inReverse、timeScale。


注意事项

  1. 默认情况下动画是全局的。 this.anims.create() 会在所有 Scene 中注册。不要在每个 Scene 中重复创建 -- 它会记录一条警告并返回已有的那个。
  2. repeat: -1 永远不会触发 animationcomplete。 使用 stop() 来结束无限动画。应改为监听 animationstop。
  3. frameRate 优先于 duration。 如果两者都设置了,frameRate 会胜出。若要控制总时长,只设置 duration(让 frameRate 保持为 null)。
  4. 每帧的 duration 是叠加的。 它是在基础 msPerFrame 之上追加的,而不是替换。
  5. play() 会停止当前动画(触发 animationstop)。如果已经正在播放,可使用 play(key, true) 跳过。
  6. 混合延迟仅在使用 play() 时有效。 playAfterDelay/playAfterRepeat 会绕过混合。
  7. 本地动画会覆盖全局。 精灵本地映射中相同 key 的动画优先。
  8. 精灵简写方法。 sprite.play()、sprite.playReverse()、sprite.chain()、sprite.stop() 是对 sprite.anims.* 的封装。
  9. 链式动画在停止后也会触发。 如果不希望如此,在停止前用 sprite.anims.chain() 清空队列。
  10. generateFrameNumbers 的 end=-1 表示最后一帧。 __BASE 帧会被自动排除。

源文件映射

文件用途
src/animations/AnimationManager.js全局单例 -- 创建、移除、获取、generateFrame*、mix、staggerPlay
src/animations/Animation.js动画定义 -- 帧、时序、yoyo、重复逻辑
src/animations/AnimationState.js每个精灵的组件 -- 播放、停止、暂停、链式调用、事件
src/animations/AnimationFrame.js单帧数据 -- textureKey、textureFrame、duration、progress
src/animations/events/index.js所有动画事件常量
src/animations/typedefs/Animation.jsAnimationConfig 类型定义
src/animations/typedefs/PlayAnimationConfig.jsPlayAnimationConfig 类型定义
src/animations/typedefs/GenerateFrameNumbers.jsgenerateFrameNumbers 的配置
src/animations/typedefs/GenerateFrameNames.jsgenerateFrameNames 的配置

评论

支持 Markdown