jamespierce

How to re-use animations for different sprite sheets?

Recommended Posts

EDIT: Click here to jump directly to my dev blog explaining my current solution. Keep reading if you want to get some valuable background information to why Phaser 3 anims work the way they do now, and why it's better.

----------------------------------------------------------------------------------------------------------------------

I have different sprite sheets that have the same sequence in the exact same frame numbers, such as:

walk = [0, 1, 2, 3]

I have checked every example in the Phaser 3 labs and all of them bind the animation to the sprite sheet. So when I would do .play('walk') it always takes the textures of the sprite sheet the animation was bound to.

this.anims.create({
	key		: 'walk',
	frames		: this.anims.generateFrameNumbers('spritesheet1', { frames: [ 0, 1, 2, 3 ] }),
	repeat		: -1,
	frameRate	: 9
});

How can I take the above animation and re-use it on any sprite sheet where the frames [ 0, 1, 2, 3 ] apply to walking?

Share this post


Link to post
Share on other sites

EDIT: See some comments later below why this workaround is not good! If you can, create all your anims at the start of the game. (Here I wanted to create anims when the object is instantiated, which could be at the start but also anytime during the game.)

-------------------------------------------------------------------------------------------------------

For now I found a work-around that works like this:

  • Instantiate object that has a sprite & name.
  • Sprite is random for every instance, but they all share the same walk animation frames: [0, 1, 2, 3 ]
  • Upon instantiating, each object creates it's own animation... said animation is bound to his spritesheet as texture
  • I created a helper function to get the animation frames by key

However, I have a hard time seeing that it is really the goal to create unique animations like this, even if they share the exact same sequence of frames.

In Phaser 2, animations could be re-used for any sprite sheet, as long as the frame sequence was identical (such as my example here). I'm sure there is a way in Phaser 3 but I just can't figure it out.

Anyways, for now this is my workaround code below. But if anyone of you knows how to do this properly, any insight would be much appreciated!

let getAnimFramesByKey = function(key)
{
    'use strict';
	
    let frames	= [];
	
    switch(key)
    {
        case 'walk':
            frames	= [ 0, 1, 2, 3 ]
            break;
        case 'run':
            frames	= [ 4, 5, 6, 7 ]
            break;
        // etc.
    }
	
    return frames;
};

// Inside the object with a sprite and name:
// this.scene is just a reference to the Phaser Scene inside which it was instantiated

// Create the object
// this.spr = Phaser Sprite Object
// this.NAME = a unique name given to this object
// this.spr_id = the id of the spritesheet that was loaded into the game, i.e. 'spr1', 'spr2' etc.
ObjectName.prototype.create = function()
{
    // Phaser Sprite Object
    let key = 'spr' + this.spr_id;
    this.spr = this.scene.add.sprite(this.x, this.y, key);

    // Animations...
    // ...check if this animation was already created
    if(this.scene.anims.anims.entries['walk-' + this.NAME])
    {
        return;
    }

    // ...create the animation, bound to the object's sprite texture
    this.scene.anims.create({
        key		: 'walk-' + this.NAME,
        frames		: this.scene.anims.generateFrameNumbers('spr' + this.spr_id, { frames: getAnimFramesByKey('walk') }),
        repeat		: -1,
        frameRate	: 9
    });
};

ObjectName.prototype.triggerAnim = function(key)
{
    // Play the animation, which is bound to this sprite sheet
    switch(key)
    {
        case 'walk':
            this.spr.play('walk-' + this.NAME);
            break;
        default: // idle
            this.spr.anims.stop();
            this.spr.setFrame(0);
    }
};

 

Share this post


Link to post
Share on other sites

@blackhawx Thank you for your reply! Unfortunately, your solution doesn't solve the problem. That's just the manual approach, which I was doing before myself: Create the animation for every sprite sheet.

However, I'm adding more and more sprites (skins) and the list of animations will grow to a huge list. It doesn't make any sense because the frame sequence is exactly the same for every sprite sheet. So why should we fill up the AnimationsManager with so many animations, when 1 would be applicable to all of them? That's what I'm trying to solve.

Unfortunately, as long as the animation is bound to the sprite sheet, there is no way to re-use it. So yeah, if that's really the case then your solution above is probably the best one. But if you have 100 skins in the game for example, that means adding 100 animations...

Since in Phaser 2 it was possible (as a matter of fact, it was the default behavior of animations), I am sure it is also possible in Phaser 3. I just don't know how and studying the lab examples & documentation is not helping me anymore >.<

Share this post


Link to post
Share on other sites

Also discussed here:

 

 

As you said, the animation system is bound to the sprite sheet texture.

How I'm thinking to go about it is to not use phaser animations entirely, rather create my own animation system that calls, this.sprite.setFrame(i),  wrapped around a setInterval for fps and such. Preferably I'm going to dig into the animation system and see if I can make it more flexible.

 

Share this post


Link to post
Share on other sites
10 minutes ago, jest said:

How I'm thinking to go about it is to not use phaser animations entirely, rather create my own animation system that calls, this.sprite.setFrame(i),  wrapped around a setInterval for fps and such. Preferably I'm going to dig into the animation system and see if I can make it more flexible.

Brilliant approach! You're right, it could be expanded or we could create a custom plugin to make it work. That's what Phaser 3 was intended for in the first place! I didn't even think about it.

Thanks for the link to the other topic. I didn't mean to create a duplicate. I searched the forum before posting but did not come across it.

Quote

So yes, v3 will re-use global animation data across as many Game Objects as you like, without duplicating all of the frames and other stuff.

In the other topic you've linked, @rich said the above thing. I am highly confused now because in v3 it appears to be exactly the opposite: You absolutely must duplicate the animation frames if they are targeting a different sprite sheet! @jest Do you know what he meant? How do you re-use global animation data in v3?

Share this post


Link to post
Share on other sites

To clarify what I said you have to think about this the other way around. Yes, you may have to create 100 unique animations in the global Animation Manager, but this is absolutely nothing in terms of memory compared to how v2 did it.

Say you have several thousand Sprites in your game, maybe even tens or hundreds of thousands over the *lifetime* of playing your game. In v2 that would literally be thousands upon thousands of identical animation data, one unique copy for every single instance of any animated sprite in your entire game.

Back to v3, yes you may have a hundred global animations, that may or may not use the same frame indexes as each other, but it’s still a fraction of the memory required compared to every Sprite carrying its own data around with it (which is what you’ll end up with if you follow your approach above). Because it doesn’t matter how many Sprites your game creates, the quantity of animations never changes.

Don’t optimise the wrong place, because unless every single sprite in your game uses a 100% unique animation, any other approach will consume more memory in the long run.

 

Share this post


Link to post
Share on other sites
5 minutes ago, rich said:

... but it’s still a fraction of the quantity of memory required compared to every Sprite carrying its own data around with it...

I knew there was something to it that was beyond my knowledge! Thank you so much for giving this background information, I really appreciate it.

I had no clue that it is actually advantageous to the performance. Since I will be adding tons of sprite sheets, I will really benefit from the way Phaser 3 does it. I will stick with it.

Thanks again!

Share this post


Link to post
Share on other sites

I have an OCD problem, can't help but want to optimize further but I guess this can be brushed aside until problems actually arise.

Chrome V8 last year shipped it's new optimizing compiler, I recall a talk where chrome devs were saying not to worry much anymore.

I still can't help but be curious as to how much memory I'll save so I should probably profile and compare at some point.

 

 

Share this post


Link to post
Share on other sites
11 minutes ago, jest said:

I have an OCD problem

I have that too xD Sometimes I get lost just optimizing my code or "beautify-ing" my code, when all I wanted to do initially was fix a bug...

10 minutes ago, samme said:

You have to remember that animations using different textures or different cells-in-texture aren't really "the same", even if the frame sequence is similar.

I know very little about the works behind animation, nor what exactly needs a lot of processing power. I should really start educating myself on such topics. Thanks for pointing this out, at least now I have some good keywords that I can google and research.

Share this post


Link to post
Share on other sites

It's not just about memory, there's a tangible cost to the set-up and creation of animation data if done at run-time too, especially if every Sprite is carrying its own baggage around with it. You want expensive operations, like creating a bunch of animations, to be done once at the start when your game is loading, and then in-game when a Sprite is assigned an animation for it to be virtually instant.

By all means, track performance and be as OCD as you like, but to then talk about even thinking of using setInterval in the same thread is like the complete polar opposite :)

Share this post


Link to post
Share on other sites
18 minutes ago, rich said:

You want expensive operations, like creating a bunch of animations, to be done once at the start when your game is loading, and then in-game when a Sprite is assigned an animation for it to be virtually instant.

Okay so that makes my "workaround" solution above terribly bad because animations are created when the object (npc with a sprite) is created! I'll go back to the way I had it before: Simple helper methods where I provide a string to return the animation-key for the corresponding sprite sheet.

18 minutes ago, rich said:

By all means, track performance and be as OCD as you like, but to then talk about even thinking of using setInterval in the same thread is like the complete polar opposite :)

I apologize for my naivety in this topic but I'm happy I asked and got all this new information now. I know now the desired approach was unnecessarily "organized" at the cost of being terribly ineffective. Maybe I can extend the Animations object with my helper methods to provide a sprite key and animations key, and it will then play the right animation for that sprite sheet, i.e. 'spr1-walk', 'sp2-walk' etc. And then I'll load all animations from a JSON file! From everything I've understood now, I think this would be a proper approach to Phaser 3.

Share this post


Link to post
Share on other sites
7 hours ago, samme said:

sprite.setData('anims', {
  down: 'alien:down'
  left: 'alien:left',
  right: 'alien:right',
  up: 'alien:up',
});

 

This is even cleaner! No need for helper functions. I still don't use the setData() methods enough in my Phaser 3 projects. Thanks!

Share this post


Link to post
Share on other sites

James, if it's not too much to ask and if it works, could you demonstrate how you would use setData in one of your ''anime' scenarios?  I'm curious to how we can take advantage of it relating to the topic...

Share this post


Link to post
Share on other sites
On 10/12/2018 at 2:24 PM, blackhawx said:

James, if it's not too much to ask and if it works, could you demonstrate how you would use setData in one of your ''anime' scenarios?  I'm curious to how we can take advantage of it relating to the topic...

Yes of course! I will post here the code snippet as soon as I've implemented it. I've just flown to Canada last night for a family visit for 2 weeks. So I don't know right now when I'll sit down and code again. But when it's done I'll post it here.

Share this post


Link to post
Share on other sites

@blackhawx Okay I just kept typing and my post was getting a bit out of hand. So I decided to just write it out as a post on my dev blog here:

How to Re-Use Phaser 3 Animations for Different Sprite Sheets

I hope this was clear and useful! Feel free to "like" the post if it was good or leave feedback how I can improve it. Thanks again for helping me understand this process!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.