MrToenails
MrToenails

Reputation: 379

Stop animation at specific frame?

I'm making a game with Phaser, and I need to have an animation for the health bar going down dynamically stop on a frame, and I couldn't find any clear documentation on the stopOnFrame() method, or the this.anims.getFrame(index) method, both of which woulden't work. I believe that stopOnFrame accepts a frame object, not a frame number but I coulden't figure out how to get that specific frame, as the getFrame() method returned undefined. If there's something I'm missing, my ideal solution looks something like this:

this.hpBar.play({key: 'damageAnimation', startFrame: this.hp})
this.hpBar.stopOnFrame(this.hpBar.getFrame(this.hp - amountOfDamage))

Thanks for any suggestions, cheers!

PS: I know there's some more nuance to how I would use the animations in forwards and reverse to properly create this effect, the example is purely for demonstration.

Upvotes: 3

Views: 633

Answers (1)

winner_joiner
winner_joiner

Reputation: 14695

Two points, you would have to pass a Phaser.Textures.Frame to the stopOnFrame function, and as far as I know the function getFrame doesn't exist (atleast on the phaser sprite GameObject, as shown in your code).

You can use the function getFrameAt on the Animation object, the index has to be here only a valid integer, in the range from 0 - to max frame of the animation. (btw.: here is the link to the documenation of the stopOnFrame function)

Info: the frame index-count, is based on the frames of the specific animation, not the frame in the spritesheet. (In this example the sprite frame index of the kick is 12, but in the kick animation the index for that specific frame is 2)

Here a Demo:

document.body.style = 'margin:0;';

var config = {
    type: Phaser.AUTO,
    width: 536,
    height: 183,
    scene: {
        preload,
        create
    }
}; 

function preload(){
    this.load.spritesheet('brawler',
        'http://labs.phaser.io/assets/animations/brawler48x48.png', 
        { frameWidth: 48, frameHeight: 48 }
    );
    this.load.image('brawler-sheet',
        'http://labs.phaser.io/assets/animations/brawler48x48.png');
}

function create () {
    this.add.text(10,10, 'Click, to Stop on kick (third frame)')
        .setScale(1.5)
        .setOrigin(0)
        .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});

    this.anims.create({
        key: 'kick',
        frames: this.anims.generateFrameNumbers('brawler', 
            { frames: [ 10, 11, 12, 13, 10 ] }),
        frameRate: 8,
        repeat: -1,
        repeatDelay: 0
    });
    
    this.add.image(140, 20, 'brawler-sheet')
      .setOrigin(0)
        
    const cody = this.add.sprite(100, 90);
    cody.setScale(3);
    cody.play('kick');
               
    this.input.on('pointerdown', () => {
        cody.stopOnFrame(cody.anims.currentAnim.getFrameAt(2))
    });
}

new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>

But for a simple healt-bar, I would use a phaser tween, not an animation, like this you can set the value more precise, you can easily update the max-health and "animation" is easy.

Here a demo how I would do it (two visula alternatives):
(I would opt for the second version, if you have some nice heart images or so, I could find a better image for the demo)

document.body.style = 'margin:0;';

var config = {
    type: Phaser.AUTO,
    width: 536,
    height: 183,
    scene: {
        create,
        preload
    },
    banner: false
}; 

function preload ()
{
    this.load.image('mushroom',
    'https://labs.phaser.io/assets/sprites/mushroom16x16.png');
}

let healthMax = 32*5
function create () {
    this.add.text(10,10, 'Click, to animate healthbar')
        .setScale(1.5)
        .setOrigin(0)
        .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
    
    this.healthbarBG = this.add.rectangle(10, 50, 32*5, 10, 0xffffff)
            .setOrigin(0);
    this.healthbar = this.add.rectangle(12, 52, healthMax - 4, 10 - 4, 0xff0000)
            .setOrigin(0);
        
    this.add.text(10, 75, 'Or abit more flashy')
        .setOrigin(0)
        .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
            
    let tilespriteBG = this.add.tileSprite(10, 100, 16*5, 16, 'mushroom')
        .setOrigin(0)
        .setScale(2);
    tilespriteBG.setTint(0x2a2a2a)

    let tilesprite = this.add.tileSprite(10, 100, 16*5, 16, 'mushroom')
        .setOrigin(0)
        .setScale(2);
                
    this.input.on('pointerdown', () => {
    
        // remove 50% health
        let newHealthWidth = healthMax * .5;
        this.tweens.add({
            targets: this.healthbar,
            width: newHealthWidth,
            duration: 750,
            ease: 'Power2',
            /* this two following properties must be remove, just for demo
            yoyo: true,
            repeat:-1*/
        });    
        this.tweens.add({
            targets: tilesprite,
            width: { to: 16 * 5 * .5, from: 16 * 5},
            duration: 750,
            /* this two following properties must be remove, just for demo
            yoyo:true,
            repeat:-1*/
        });
     })
}

new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>

modifying the ease and/or the duration property (and others), you can make the animation suitable (and spicy) for the most usecases.

Upvotes: 4

Related Questions