Jump to content

Spritesheet animation


BrunoHautenfaust
 Share

Recommended Posts

After 5 hours I still can't get a decent animation. My eyes and head already hurt. And there I thought this was supposed to be a breeze.

 function preload () {            game.load.spritesheet('idle', 'resources/standing/krn_standing.png', 138, 359);            game.load.spritesheet('running', 'resources/running/krn_running.png', 321, 357);        }function create () {            player = game.add.sprite(game.world.width / 2, 0, 'idle');            player.animations.add('idle');	    player.animations.add('running');}function update() {        	 if (controls.right.isDown) {        		player.body.velocity.x = 150;        		player.animations.play('running', 10, true);        	}        	 else {        		player.body.velocity.x = 0;		    	player.animations.play('idle', 10, true);        	}     	        }}

player.add.sprite loads a spritesheet and uses it only. Regardless of the fact that I add other spritesheets and animations. Only the idle sequence is being played.

I've tried cramming 'idle' and 'running' in one png file and defining with an array from which frame to which to do what. The result - the entire sheet played and disappeared. Even though it was set to loop. Can't reproduce that as I don't remember how I did it. 

Link to comment
Share on other sites

I'm not specifying the frames because the whole spritesheet contains all the needed frames. I have one spritesheet for 'idle' and another one for 'running'.

 And what I'm trying to do is:

 if (moving right) { player.animations.play('running', 10, true); }

 else { player.animations.play('idle', 10, true); }

   Basically, the object sees only this spritesheet: player = game.add.sprite(game.world.width / 2, 0, 'idle');

And even if I tell the object to move and change the animation(loaded from another spritesheet), it won't do that.

Link to comment
Share on other sites

Hi, you have to change texture from idle to running and back.

 

It is also good to track if character is running or not, because loadTexture can do some allocations and you are currently triggering animation on every update. It is OK, as triggering the same animation that is currently running is ignored, but you are also changing texture...

    update() {        // shortcut        var keyboard: Phaser.Keyboard = this.game.input.keyboard;        if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {            if (!this.running) {                this.player.loadTexture("running")                this.player.animations.play('running', 10, true);                this.running = true;            }            this.player.body.velocity.x = 15;        }        else {            if (this.running) {                this.player.loadTexture("idle")                this.player.animations.play('idle', 10, true);                this.running = false;            }            this.player.body.velocity.x = 0;        }    }
Link to comment
Share on other sites

 

Ah, thanks. That wasn't clear to me. 

 

try adding a stop command before you start the new animation. 

 

I already did. It freezes the animation altogether.

Right now I'm trying(again...) to use one spritesheet:

 

game.load.spritesheet('sheet', 'resources/sheet.png');   << So far, so good

var idle = player.animations.add('idle', [0,1,2,3], 10, true);   << Something's wrong here. I think it's because there's no connection to 'sheet'. And I don't know how it should be established.

player.animations.play('idle', 10, true);     << Most likely here, too

And I get Cannot read property 'index' of undefined

 

I read the documentation about Animations: packed with arguments which nobody seems to use. And I've been looking at source codes, googling, youtubing and swearing this whole beautifully wasted day. 

 

EDIT: TomAtom, what is 'this' in this case?(No pun intended). The update method? Also, isn't loadTexture() kinda heavy on resources?

 

EDIT 2:

OK. Getting a bit closer(or not). I switched to Phaser.CANVAS. Because WebGL is giving me a hard time.

Here's what I got:

- game.load.spritesheet('sheet', 'resources/sheet_old.png', 321, 360);

- player = game.add.sprite(game.world.width / 2, 0, 'sheet');

function update() {		    controls = game.input.keyboard.createCursorKeys();        	game.physics.arcade.collide(player, platforms);	        	// controls:        	if (controls.left.isDown) {        		// left        		player.body.velocity.x = -150;        	}        	 else if (controls.right.isDown) {        		player.body.velocity.x = 150;        	        	player.animations.play('sheet', 19, [8,9,10,11,12,13,14,15]);        	}        	 else {        		player.body.velocity.x = 0;		   		    player.animations.play('sheet', [0,1,2,3], true);        	}        	if (controls.up.isDown) {        		player.body.velocity.y = -350;        	}        }

As I have both idle and running sequence on one spritesheet, no matter what I do, it keeps playing all the frames.

 

EDIT 3:

 I tried with the 'dude' sprite from the tutorials and it worked. Guess the spritesheet size is crucial for all this crap to work. But wait... no. It works now. Oh, COME ON!!!! A whole day wasted and NOW it works!? Somehow. It appears that adding more arguments to the game.animations.play(<desired animation>), other than just the desired animation,can break things tremendously. I am too tired to be confused...  -_-

Link to comment
Share on other sites

TomAtom, what is 'this' in this case?(No pun intended). The update method? Also, isn't loadTexture() kinda heavy on resources?

 

 

1) code I pasted in my answer was written in Typescript. But the idea should be clear - changing texture to needed one and doing so only if necessary ...

 

2) loadTexture is way how to switch texture if you have animations in different spritesheets. Although it says "load" it may not do any loading from disk or so as Phaser looks into cache if texture is already there. Anyway, there is some overhead and it is always better to create spritesheets with all needed frames or even better to use atlases as you can get rid of empty spaces (altases are much better packed than spritesheets).

Link to comment
Share on other sites

 

1) code I pasted in my answer was written in Typescript. But the idea should be clear - changing texture to needed one and doing so only if necessary ...

 

2) loadTexture is way how to switch texture if you have animations in different spritesheets. Although it says "load" it may not do any loading from disk or so as Phaser looks into cache if texture is already there. Anyway, there is some overhead and it is always better to create spritesheets with all needed frames or even better to use atlases as you can get rid of empty spaces (altases are much better packed than spritesheets).

 

 

 I tried loadTexture. It really does switch spritesheets. Neat! But maybe the best option as you all recommend is trying this atlas thing.

 

 

Use a atlas or a big spritesheet with all frames and you don't need change the texture my friend  :)

 

To improve your code, you can change

player.animations.play('sheet', 19, [8,9,10,11,12,13,14,15]);

for this

player.animations.play('sheet', 19, Phaser.ArrayUtils.numberArray(8, 15));

Regards, Nicholls

Thanks, Nicholls! :) I was wondering how to set an interval for the frames. 

Currently I'm trying out the atlas. It's not as complicated as I thought.

I used this to export a JSON file with ShoeBox. Now I have a main.png and main.JSON. OK.

 

 Yet, I can't get it to work. I only get the first frame.

game.load.atlas('main', 'main.png', 'main.json');  // Atlas preloadedplayer = game.add.sprite(game.world.width / 2, game.world.height / 2, 'main');player.animations.add('idle', Phaser.Animation.generateFrameNames('Krn', 0, 7, '', 3), 30, true); // The filenames are Krn_000 up to Krn_007. I even tried with "Krn_" as the first parameter in generateFrameNames().player.animations.play('idle');   // Doesn't work. I'm getting Uncaught TypeError: Cannot read property 'index' of undefined (phaser.js:68763)

I took a peek at phaser.js. As far as I understood, for some reason it can't iterate over the frames. But why?

Link to comment
Share on other sites

I see. I should've removed the file extensions. And I did. Then I added that underscore. And it worked. But the animation is jiggly. Moving back and forth by a few pixels. Probably due to the x, y coordinates from 'frame' and/or 'spritesource'. They differ. Could it be from the texture padding? I hope there's an elegant fix for that? Because I'd sure hate to manually edit the spritesheet and test whether it works.

I'm quite positive that most of the sprite problems I'm having are due to the fact that I'm using separate files for each frame. Which I pack in a spritesheet. But the final spritesheet is not quite as it should be. So far the sprites are 8 in total. Ordered in 2 rows and 4 columns. And some empty space in the bottom and right.

Here's the edited JSON file:

{"frames": [	{		"filename": "Krn_000",		"frame": {"x":0, "y":358, "w":133, "h":356},		"spriteSourceSize": {"x":331,"y":222,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_001",		"frame": {"x":0, "y":0, "w":138, "h":357},		"spriteSourceSize": {"x":326,"y":221,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_002",		"frame": {"x":134, "y":358, "w":133, "h":358},		"spriteSourceSize": {"x":331,"y":220,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_003",		"frame": {"x":268, "y":0, "w":132, "h":359},		"spriteSourceSize": {"x":332,"y":219,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_004",		"frame": {"x":268, "y":360, "w":128, "h":359},		"spriteSourceSize": {"x":336,"y":219,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_005",		"frame": {"x":401, "y":0, "w":122, "h":358},		"spriteSourceSize": {"x":342,"y":220,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_006",		"frame": {"x":397, "y":360, "w":122, "h":357},		"spriteSourceSize": {"x":342,"y":221,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	},	{		"filename": "Krn_007",		"frame": {"x":139, "y":0, "w":128, "h":356},		"spriteSourceSize": {"x":336,"y":222,"w":800,"h":640},		"sourceSize": {"w":800,"h":640}	}],"meta": {	"image": "main.png",	"size": {"w": 1024, "h": 1024},	"scale": "1"}}
Link to comment
Share on other sites

Using separate images and turning them into a spritesheet is the correct way to do it. I've had spritesheets made from more than 40 images and they work really well. I'm surprised though : how come your sprite be squared (1024x1024) with 2 rows and 4 columns? You must be wasting a lot of memory for useless transparent space (I guess).

 

How did you pack it? Did you use TexturePacker? There's a free version which really does a great job.

 

And Damn I can't remember the exact structure of the JSON (don't have my code now), but what's the difference between frame and spriteSourceSize? Why those 2 fields?

Link to comment
Share on other sites

I'm using shoebox. There were a lot of online spritesheet generators. I tried a few including leshylabs. But I was having problems properly animating while using game.load.spritesheet. I must've thought it was due to the spritesheet packer or something. Can't remember.

 Anyway, you're right. There's a lot of wasted empty space. But I plan on adding more frames. As this point I just want to get the idle animation going correctly.

 

Here's what I've found out:

 

We have a sprite and empty space around, like so:

Sprite
"frame": {"x":0, "y":358, "w":133, "h":356}, -> X and Y are the starting coordinates for each frame in the spritesheet, w and h are width and height of the frame.
"spriteSourceSize": {"x":331,"y":222,"w":800,"h":640},  -> X and Y are the starting coordinates for the sprite. W and H is the whole size of the file. The rest is empty space.
"sourceSize": {"w":800,"h":640} -> original size of each sprite imported in the sheet. I think I should crop them. Shoebox has such option.
 
Or another way to look at it:
"spriteSourceSize": {"x":331,"y":222, <sourceSize>},
 
I don't think I'm supposed to edit those manually. Feels like a waste of time.
 
EDIT: Now I saw that the option for spritesheet export + JSON in Shoebox messes with the order of the files. Because of their names. So I should either change them manually or give that texture packer a go.
...TexturePacker's not bad.
And what's the difference between JSONArray and JSONHash?
EDIT2: For some reason exporting Phaser JSONArray file lead to Cannot read property 'index' of undefined.
But using JSONHash worked. The spritesheet is smaller in size. Correctly arranged. And the animation is right. :) So far so good!
 However, I do need to point out that I had to edit the sprite.body size. Initially, the body size is quite big. But player.body.setSize(w, h, x, y); did the trick.
 And wouldn't you know it - importing more frames and assigning them different animations works flawlessly!
This atlas thing is amazing!  :D Only one question - is there a program that quickly removes sprite extensions in the JSON file? If not I suppose I could write one.
 
 
Link to comment
Share on other sites

Thanks, Tom! :)

---

 By the way, earlier I said that exporting Phaser JSONArray through TexturePacker gave me an error when trying to use it. It might have forgotten to save the file when I removed the file extensions in the JSON.

As for what's the difference between JSONArray and JSONHash, I found out the following:

Which of the two Phaser exporters you choose does not really matter. They contain the identical data with some different layout. It's just the loading instruction that is different. For now use Phaser (JSONHash).

I'm just putting this here if anyone else wonders.

 

EDIT: I found where the trimming sprite names option is in TexturePacker: Trim sprite names. Just needed to expand the Data settings.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...