You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phaser 3中如何实现一个动画结束后立即播放另一个动画?

解决Phaser中启动走路动画后循环播放走路动画的问题

问题根源

你的代码在update函数中,只要左右键按下,就会每帧重复执行this.soma.anims.play('startWalking')和this.soma.anims.chain('walking', true)。这会导致startWalking动画被不断重置,永远无法播放完成,自然触发不了后续的链式动画。

修复方案

我们可以通过状态变量跟踪动画状态+动画完成事件来实现需求,确保startWalking只在需要时播放一次,完成后自动切换到循环的walking动画。

修改后的完整代码

const velocity = 100;
export class Game extends Phaser.Scene{
    constructor(){
        super({key: 'game'})
    }
    init(){
        this.isWalking = false; // 跟踪是否处于走路状态
    }

    preload(){
        this.load.image('soma', 'img/soma.png')
        this.load.image('placeholder', 'img/placeholder.png')
        this.load.spritesheet('walking-anim', 'img/walking-anim.png', {frameWidth: 32, frameHeight: 34})
    }

    create(){
        this.soma = this.physics.add.sprite(100, 10, 'soma')
        this.placeholder = this.physics.add.sprite(100, 150, 'placeholder').setImmovable();
        this.placeholder.scaleX = 20
        this.physics.add.collider(this.soma, this.placeholder)
        this.placeholder.body.allowGravity = false

        this.anims.create({
            key: 'startWalking',
            frames: this.anims.generateFrameNumbers('walking-anim', { start: 19, end: 20}),
            frameRate: 10,
            repeat: 0 // 确保只播放一次,默认就是0可以不写,但明确标注更清晰
        })

        this.anims.create({
            key: 'walking',
            frames: this.anims.generateFrameNumbers('walking-anim', { start: 21, end: 37}),
            frameRate: 10,
            repeat: -1 // 设置为-1表示无限循环
        })

        this.anims.create({
            key: 'idle',
            frames: this.anims.generateFrameNumbers('walking-anim', { start: 1, end: 3}),
            frameRate: 2.3,
            yoyo: true
        })

        this.up = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
        this.left = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
        this.right = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);

        this.physics.world.setBounds(0, 0, 240, 160);
        this.soma.setCollideWorldBounds(true)

        // 监听startWalking动画完成事件
        this.soma.on('animationcomplete', (anim) => {
            if (anim.key === 'startWalking') {
                this.isWalking = true;
                this.soma.anims.play('walking');
            }
        });
    }

    update(){
        this.soma.body.setVelocityX(0);
        const isMoving = this.left.isDown || this.right.isDown;

        if (this.left.isDown){
            this.soma.body.setVelocityX(-velocity);
            this.soma.flipX = true;
        }

        if (this.right.isDown){
            this.soma.body.setVelocityX(velocity);
            this.soma.flipX = false;
        }

        if (isMoving) {
            // 如果没在走路状态,且当前不是在播放startWalking,就启动启动动画
            if (!this.isWalking && this.soma.anims.currentAnim?.key !== 'startWalking') {
                this.soma.anims.play('startWalking');
            }
        } else {
            // 松开按键后重置状态,播放 idle 动画
            this.isWalking = false;
            this.soma.anims.play('idle', true);
        }
    }
}

关键修改点说明

  1. 添加状态变量isWalking:用于跟踪角色是否已经进入持续走路状态,避免重复触发启动动画。
  2. 动画完成事件监听:在create中给角色绑定animationcomplete事件,当startWalking播放完成后,自动切换到循环的walking动画,并标记走路状态。
  3. 优化update中的动画逻辑:只有当角色开始移动且未处于走路状态、当前也没在播放启动动画时,才播放startWalking;松开按键后重置状态,切回idle动画。

另一种简洁方案:正确使用anims.chain

如果你更倾向于用链式动画,只需确保play和chain只被调用一次,而不是每帧重复执行。可以通过判断当前动画是否是目标动画来实现:

// 在update的isMoving分支中替换为:
if (!this.soma.anims.isPlaying || this.soma.anims.currentAnim.key === 'idle') {
    this.soma.anims.play('startWalking');
    this.soma.anims.chain('walking');
}

这种方式的核心是避免每帧重复调用play和chain,只在从idle切换到移动时执行一次链式设置。

内容的提问来源于stack exchange,提问作者slvr402

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:40:41