How Does {sprite}.anims.remove() Actually Work?

I have a card game that animates the elements on the card after the flipping animation is done. It took me two weeks to figure out that I can’t simply do this:

var card = //a loaded sprite
var card1frames = game.anims.generateFrameNames(//etc...
var cardAnimationFrames = game.anims.generateFrameNames(//etc...

function dealCards(randomCard) {
game.anims.create({ key: 'cardflip', frames: card1frames, frameRate: 12, repeat: 0 })
game.anims.create({ key: 'cardanimation', frames: cardAnimationFrames, frameRate: 12, repeat: -1 })

card.anims.play('cardflip')

card.once('animationcomplete', () => {
card.anims.play('cardanimation')
game.anims.remove('cardflip')
  }
}

If I do this, the card flips, and then plays the animation once, but if I call dealCards() again, even though it receives a new card, it will always play the card animation for the last card. It will never change. I have to do this:

function dealCards(randomCard) {
    card.anims.remove('cardflip') //Note the use of the sprite name card instead of game.

    game.anims.create({ key: 'cardflip', frames: card1frames, frameRate: 12, repeat: 0 })
    game.anims.create({ key: 'cardanimation', frames: cardAnimationFrames, frameRate: 12, repeat: -1 })
    
    card.anims.play('cardflip')

    card.once('animationcomplete', () => {
    card.anims.play('cardanimation')
    game.anims.remove('cardflip') //Note the use of game instead of the sprite name.
  }
}

If I remove the cardflip animation with the sprite name card, then weird errors occur. What I want to know is why does removing by the canvas name of the last animation played not actually remove the sprite? Why does it get stuck? If I don’t add a second animation (to animate the card elements), then nothing bad ever happens. The correct card always flips up.

And why does calling remove on the sprite itself work? Why do I have to call it on the sprite?

I can clarify if this is unclear.

Create each animation only once, in create().

Don’t remove any animations.

dealCards() receives randomCard but it modifies card instead. If card is never reassigned, it will always reference the same sprite.

Maybe you want just

sprite.anims.play('cardflip').anims.chain('cardanimation');

Create each animation only once , in create() .

Don’t remove any animations.

I don’t think this is a good strategy as this will lead to really bloated and confusing code. I would have to enumerate each card animation for each card. In other words, I will need to create 52 animations for each card position, plus their animations.

I now have a multiatlas of the card deck with each card animation in a folder. So the 2c has a folder, the 3c has a folder and so on. I shift each hard out of an array like this:

 let c1frameNamesDraw = game.anims.generateFrameNames('cards', { start: 1, end: 7, zeroPad: 0, prefix: draw.drawcards.shift() + '/', suffix: '' })
 game.anims.create({ key: 'c1animationdraw', frames: c1frameNamesDraw, frameRate: 18, repeat: 0 })

The shift will be a string of poker notation as referenced above, so the King of clubs will come as Kc, and that’s also the folder name.

Maybe you want just

sprite.anims.play('cardflip').anims.chain('cardanimation');

This was my very first solution, and will work, but there was some weirdities that I forget. You still have to remove the sprites to clear them for the next hand.

I understand the workflow of card games probably wasn’t considered for a gaming engine. I just want to figure out how it works for my own sanity.

Perhaps there is a way to enumerate all the possibilities in a shorter way than brute force dumping them all in a row, but I’m certain it would be more convoluted than just passing the returned shuffle array of card names like [2c, 8s, Th, Kc, As] and shifting them (or popping if you are a dirty base dealer :stuck_out_tongue: ).

Animations are global, like assets. They refer only to textures and frames, not sprites.

sprite.anims.remove(), “Handle the removal of an animation from the Animation Manager”, doesn’t remove an animation or anything else. If you want to remove (i.e., delete) a sprite’s animation, use

this.anims.remove(sprite.anims.currentAnim.key)

You have to hide the sprite or destroy it. Removing an animation doesn’t do that.

I’m with @samme - I would create all the animations up front, at the start and then call them as needed in-game. 52 animations doesn’t sound like many to me. To be honest, several hundred wouldn’t matter either, they’re tiny in terms of memory footprint and much cleaner than constantly creating and removing them. All you’d actually need is a key to play them that you define, so it can be as clear as you make it!

Removing an animation will not remove the Sprite it was playing on, because animations are global. You could have hundreds of sprites all using the exact same animation, at the same time. Which is why animations themselves have no concept of which sprites are actually using them. Only the sprite knows that.

What is the difference between game.anims.remove(), and sprite.anims.remove()? I have no idea why when I play an animation in place of an existing animation that I need a different selector to remove.

It’s a lot more than 52 animations, it is multiplying everything by 52. So in this game I will need an animation for each card flip for both the deal and the draw, then 8 action animations, and the nightmare of changing everything. 4160 declared animations brute forcing versus ~40 now. I get you it’s only about 500KB, but it’s clunky.

Edit: Also appreciate this answer. I’m not the only one I know wanting to know the best pattern. I have got it to work with my removal method, but it was just stubborness that I found a workaround.

sprite.anims.remove doesn’t do what you think it does, it just stops the animation on the current sprite. Once an animation is created, it’s stored globally, for any sprite to use. So to remove it, it needs removing from the global Animation Manager, which is what this.anims is from within a scene (or game.anims, although you should never use that access method really).

I’m very surprised that you would have animations for every single card if these are a standard deck of cards? (i.e. not a Magic the Gathering style game, but poker or blackjack) - personally I would have split each card up, so the front and back is one sprite, the suit is another and the card value another. So a single card would comprise 3 layers. Then you get to reuse all the layers across the whole deck, which is far, far less total animations to deal with - and less textures, too!

I get that it’s probably too late to change now, though!

I didn’t think it did anything. I just took a wild guess and had things work. :slight_smile:

I think that makes sense. When I play the cardflip, I’m only wanting to remove the card itself. Now when a wild card comes it plays the animation of the wild card, but it’s not actually a card, it’s an animation of the card face up. I check if it is a wild card, and if it is, the animation cycles until the player hits the deal button.

I’m very surprised that you would have animations for every single card if these are a standard deck of cards? (i.e. not a Magic the Gathering style game, but poker or blackjack) - personally I would have split each card up, so the front and back is one sprite, the suit is another and the card value another. So a single card would comprise 3 layers. Then you get to reuse all the layers across the whole deck, which is far, far less total animations to deal with - and less textures, too!

I would agree with you doing spritesheets oldschool, but with texture packer, the identical cards are mapped. So it means if I duplicate the card back 52 times, it only gets saved in the multiatlas once! It’s an amazing feature which is a huge selling point of Phaser 3.

I messed around with programmatically flipping the cards, but it doesn’t look good compared to sprite sheets. Since that is the focus of the game, it’s important to build suspense by actually showing what is turning. Five frames seemed to be ideal, which doesn’t include the face or the back.

1 2 3 4 5 6 7

This is a a rough idea of what I’m doing. I put off animating the card elements because it was messing everything up before. As you can see now, the card changes, everything is fine. It’s a little laggy cause remote to the DB, but the game is lightning fast on my test server. Really happy with it.

The wild card is being animated with the animation. I use the a standard flip, then once it finishes it plays. I would like to have animations for the cards based on the ranking.

Have you considered using tweens like this guy did? I have to say, your animations look pretty nice and smooth!

It’s a cool article I saw before deciding to do it this. It’s for Phaser 2. It would nice to see it updated for Phaser 3. I like the snap and pop of his animation. That guy seems to have a really good mind for tween acceleration, because I see so many examples of doing it linearly.

It really depends on the style you are going for, but that solution introduces jaggys (aliasing) and not nearly as good for suspense as having ad hoc animations for each card. It’s also a lot simpler to just do in after effects. It takes maybe five minutes, so I think for most people that animating in after affects with sprites is better both ways. I made a psd with smart objects to automatically generate the cards from any symbol. Then you just open the file in AE and animate it out. I could action that out too if it became a regular thing.

The advantages of doing it programmatically is that it is lightweight, and if you are doing something pixel based, it could even add to the feel of the game.

1 Like