Jump to content

Spriter skeletal animations in Phaser


Tom Atom
 Share

Recommended Posts

 Hi, after few days of work I managed to play skeletal animations exported from Spriter tool in Phaser:

 

 For anyone who is interested I wrote article about it here: http://sbcgamesdev.blogspot.cz/2015/09/phaser-tutorial-phaser-and-spriter.html  You can see working example on the page and you can also download whole MSVC 2015 project with full source there.

 

 Currently tweening between bones and sprites is only linear. Other curve types has to be implemented yet.

 

 Also features specific for Spriter Pro version are not implemented (like events, sounds, character maps, ...)

 

 Good news is, that it should work for both WebGL and Canvas.

 

 

EDIT : added support for curves on timelines - currently supported is instant, linear, quadratic, cubic. And also added support for animating anchors on sprites.

 

post-12887-0-41986200-1441140565.jpg

Link to comment
Share on other sites

Good job!

 

I am also making a Spriter animation runtime for Panda.js (it only requires PIXI.js, so it can also be used directly as a PIXI.js plugin), you can see it here: https://github.com/LesserPanda-Engine/LP-Spriter

It is based on Spriter.js which has curve based tween support and I've already finished part for events and tags. It is still under heavy development, the next thing I am going to do is the "animation blending" which seems quite useful but not necessary for my recent games so I plan to work on that after a few weeks. Hope it can help you :D

Link to comment
Share on other sites

Thanks!

 

 I will definitely look into your code.

 

 Blending is good idea. I see two approaches:

- easy one - from current position interpolate to 1st keyframe of next animation in some constant time. This will remove break when animation is changed, will be easy to implement and also not heavy on computation,

- complex one - from current position continue animation and simultaneously start new animation. With some interpolation change weight of final result from actual animation to new animation. This will need some additional coding (running two animations at a time) and it will also need double computation during blending time.

 

 Which one are you considering for your implementation?

Link to comment
Share on other sites

This is awesome. What are the performances compared to normal spritesheet animation? Is there any plan to merge it into Phaser?

 

 

 Compared to spritesheet or normal sprite animation, the performance is worse. It is because each part of the character is sprite itself - character in example has 15 sprites. A beside it it has also 15 bones, that are making the skeletal animation. Each of these parts has its own timeline with keyframes and during time its properties are tweened from one keyframe to another. The properties are: position, scale, rotation, pivot, alpha. With focus on performance I am checking which property has to be tweened and create some internal update mask, so only necessary tweens are running.

 On the other side, such animation needs really little assets (as character is cut into small pieces and you do not need frames) and it can also be very smooth, as you are getting all the positions between two keyframes (it is framerate dependent).

 

 To use it with Phaser, you can download source (Typescript) add it to your project and use it... To be honest, if I knew how to create plugin for Phaser, I would do it :) ... can you point me to some tutorial?

 

Link to comment
Share on other sites

Of course I was expecting the performances to be worse, but I wondered by how much... Because as you said, this can help save a lot! When I see my enormous spritesheets I can't help but think that this (skeletal animations) should the new standard.

 

For the plugin I'm sorry but I can't help... I've never done that. Maybe Rich or someone else could point you to a good tutorial?

Link to comment
Share on other sites

  • 2 months later...
  • 4 months later...
Quote

Is there a way to utilize your Spriter Player for Phaser *without* Typescript?  I suspect not, but should ask.

 It should work if you get SpriteExample.js file in js folder. Spriter Player is separated from example, so it should be enough to delete "namespace" SpriterExample (from line 3483 on: https://github.com/SBCGames/Spriter-Player-for-Phaser/blob/master/Spriter/js/SpriterExample.js#L3483). Then you will have .js "library" with namespace "Spriter."

Link to comment
Share on other sites

@ritherz: working with Typescript is easy and if you use JS, you will have no problems to read TS - it is still more or less JS, but with types and more C#/Java organization when it comes to objects and classes.

To use the code, do what I suggested above to create "lib" (delete all SpriteExample). Then create your Phaser game and in Preload state put this:

        preload() {
            // test - load atlas with animation parts
            this.load.atlas("TEST", "Atlas.png", "Atlas.json");

            // load xml animation data
            // this.load.xml("TESTXml", "TEST.xml");
            // or json data - depending on your Spriter export
            this.load.json("TESTJson", "TEST.json");
        }

This is just regular Phaser loading of atlas and xml/json

Then in crate method of your state (Test state in source) put this:

            // create object that understands loaded xml or json and can create internal object structures from it. This class can be used to precess many xml/json files with Spriter data
            var spriterLoader = new Spriter.Loader();

            // create object that takes loaded xml or json and wraps it into SpriterFile (SpriterXml and SpriteJSON are descendants of SproiterFile)
            //var spriterFile = new Spriter.SpriterXml(this.cache.getXML("TESTXml"));
            var spriterFile = new Spriter.SpriterJSON(this.cache.getJSON("TESTJson"));

            // process loaded xml/json and create internal Spriter objects - these data can be used repeatly for many instances of the same animation
            var spriterData = spriterLoader.load(spriterFile);

            // Spriter anim is in SpriterGroup object, which is Phaser.Group - here you are creating one instance of Spriter animation from loaded data with atlas "TEST"
            this._spriterGroup = new Spriter.SpriterGroup(this.game, spriterData, "TEST", "Hero", 0, 100);
            this._spriterGroup.position.setTo(420, 400);

            // add it to Phaser scene graph
            this.world.add(this._spriterGroup);

 ... that is all.

 

Link to comment
Share on other sites

Thanks so much man!  I seriously can't wait to try it.

I bought a bunch of art assets with pre-built sprite animation sheets- but it also contains the separate parts.  Im getting tired of the same shitty animations with my inability to "art".

I want to be able to sell vanity equipment in my game, learning to do this is essential!

Link to comment
Share on other sites

I have finally managed to replace The Hero example stuff with my own sprite!!  I had to rename the entity in the xml I generated, and actually create a sprite atlas for the images I am using (thanks ShoeBox).

I totally see what you mean by the export coordinates/axis issues.  But, I finally figured it out!  You basically have to set pivot_y to 1 - pivot_y for all object tags.  Then the animation works well, and is not distorted like my previous attempts were.

            <timeline id="2" name="Right Arm">
                <key id="0" spin="0">
                    <object folder="0" file="2" x="120" y="10" pivot_x="0.982419" pivot_y="0.252345" angle="89.949121"/>

 I think I may write out a newbie guide on how to use this awesome tool!

LOTS OF EDIT, as I was figuring this out.

Screen Shot 2016-04-13 at 10.36.24 PM.png

 

Link to comment
Share on other sites

On 4/14/2016 at 11:14 PM, ritherz said:

 I think I may write out a newbie guide on how to use this awesome tool!

@ritherz, dude, please do share your findings using this code for Spriter and Phaser.  I've been messing with it endlessly for the past week and have the recurring issue of only one sprite displaying in the places of all the sprites in an animation -- e.g. all left feet sprites in the places where the body, head, arms and other legs should be, but they're animating properly.  The browser console displays "Cannot set frameName: <the sprite's name>" -- the sprite's name displays correctly, just not the actual sprite rendering itself.  Using sprite0 I think.  Have been all over Phaser internals trying to understand the issue.  No luck after a week.

The info out there is so sparse for this, and tends to be "here's the code, do this and you're done" which is probably true for some.  I'm attempting to use a pre-rigged animation made for/with Spriter and running into the problem mentioned above.  I've been all over the JSON and XML (or SCON and SCML) data files, comparing them with other working versions and find no practical differences.

Tutorial, YES! :)

Link to comment
Share on other sites

Yipes!  I actually dealt with this issue a few times today as I was porting more skels into my game hehe.

Im fairly sure it was due to the name of the body parts in the sprite atlas.  If you used shoebox like me, it names the individual sprites "bodypart.png" in the sprite atlas.  Try removing the extensions (.png usually) within the atlas (json dict). Also remember to refresh your cache before coming back :P 

I will definately be doing a tutorial on it, im just wanting to get my code to the point where im comfortable telling others how to do it.  Hopefully i'll have time tomorrow, but for sure sometime next week.

 

Tom wrote some awesome code here, it required very few tweaks to work for me!

Link to comment
Share on other sites

@gmalone, @rithez - I updated whole solution and split it into two projects on GitHub (https://github.com/SBCGames/Spriter-Player-for-Phaser). Player is now separeated from Test example. More, there is "Build" folder, where are spriter.js and spriter.min.js files you can take and use in your game. These files are pure player in javascript without test example.

 I also wrote tutorial on setting basic animation and it describes features of the player: http://sbcgamesdev.blogspot.cz/2016/04/phaser-tutorial-using-spriter-player.html

@gmalone: As ritherz wrote: problem is in extension. Spriter player uses names without extension. Some atlas tools add extension. If I remember well Texture packer had some option, whether to include it or not? For atlases I am using my own tool and there you can name sprite as you wish (default is file name without extension). I bet the sprite you got displayed was first sprite in atlas? This often says, that Phaser could not find requested frame and so used the first one.

@rithers: 

Quote

You basically have to set pivot_y to 1 - pivot_y for all object tags

 no changes in Spriter export should be needed. I am taking what Spriter exported and playing it in game (other thing is, that Spriter is pretty crapy and often produces trash in export).

 

Link to comment
Share on other sites

6 hours ago, Tom Atom said:

 no changes in Spriter export should be needed. I am taking what Spriter exported and playing it in game (other thing is, that Spriter is pretty crapy and often produces trash in export).

You're right, I have now gotten it to a point of just being able to save the file (.scml), and my updates take effect.

I had to change that one line here: https://github.com/SBCGames/Spriter-Player-for-Phaser/blob/master/Build/spriter.js#L1368

to:

                    info.pivotY = 1- this.parseFloat(keyDataElm, "pivot_y", file.anchorY);

You may have fixed it now, as I see you've made some changes since the version I tried on.

I'm glad you added signals, no point in me doing a writeup, great job!

Link to comment
Share on other sites

@ritherz: changing that line to:

info.pivotY = 1- this.parseFloat(keyDataElm, "pivot_y", file.anchorY);

is not OK. File object does not have anchorY property at all, so it will be undefined. Above call to parseFloat says: get "pivot_y" from keyDataElm and if this element does not contain it, then return default value (last parameter).

Original line is:

info.pivotY = 1 - this.parseFloat(keyDataElm, "pivot_y", 1 - file.pivotY);

and logic behind it is: Spriter has y axis pointing up, so pivot 0,0 is in bottom left corner. In Phaser this kind of pivot is called anchor and 0,0 is top left corner. When Spriter player decodes Spriter xml/json file it creates File objects (File is in fact sprite info in Spriter, but not sprite itself - thus it does not have anchor or scale or ...), reads its deafult pivot (in Spriter coordinates) and converts it to Phaser coordinates (with 1 - pivotY).
 Timeline keys (which the above line is from) can override deafult pivots (as you can animate change of pivot in Spriter). So, when Spriter animiation is read and there is change in pivot then it is again first read with Spriter coordinates (from Spriter xml/json). Again, I am converting it into Phaser coordinates - this is reason, while it is 1 - parseFloat(). If there is no change of pivot in animation, I am passing default pivot as third parameter. As this deafult pivot is already in Phaser coordinates (read from FIle object), I first have to switch it back into Spriter coordinates, so either "pivot_y" exists and is read in Spriter coordinates from xml/json or default value is used with Spriter coordinates. In the end any of these two is converted into Phaser coordinates (1 - parseFloat()).

 

Link to comment
Share on other sites

Glad I checked back here... I had made the mopey sad decision to generate regular spritesheets from Spriter->TexturePacker... the old fashioned way (lol!).  But you've revived my hopes again.  And, sure enough, there are .png file extensions in my troublesome JSON atlas file.  I'll review what you've (both) said and posted and give it another whirl.

Many thanks.  Will let you know.

hopeful.png.43f37bfed8afa868693d87efb73d

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

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