alex_h

Pixi + DragonBones

Recommended Posts

Not sure if this is something that's been done already, but I've put together an adapter to run dragon bones skeleton animations with Pixi content.

 

http://www.alexh.org/pixiDragonBones/

 

The source is here if anybody would like to make use of it

 

http://www.alexh.org/pixiDragonBones/pixiDragonBones.zip

Share this post


Link to post
Share on other sites

This is amazing, thank you so much! If I can get it to work correctly with Phaser (and there's an example made from your adapter in the examples repo that I managed to fix), this could be a game-changer for my anims.

Share this post


Link to post
Share on other sites

Yeah the phaser version should also be working:

 

http://www.alexh.org/phaser_bones/

 

I got an email today that the pull request for it to be added to the phaser examples had been accepted so I guess it will soon be online. Actually some of the utility methods I added for the pixi version are a bit neater than in the phaser one though, for example the method to parse texturepacker atlas json into dragonbones format now accepts both JSON hash and JSON array format in the Pixi version. I should probably update the phaser copy too.

Share this post


Link to post
Share on other sites

yeah, got it to work with my own animation in phaser. AWE-SOME. I haven't delved too deeply into it yet, but it looks like only translation and rotation are currently applied? I have an anim that uses stretching and that gets lost :(

Share this post


Link to post
Share on other sites

It's quite possible that I missed scaling, I don't think any of the animations I tested with included it. Shouldn't be too hard to add in though. I'll have a look and let you know.

Share this post


Link to post
Share on other sites

hehe...  I did some more digging, and apparently, if the advanceTime call gets passed a negative value, it implements the exact behavior with getTime that you left as an exercise for the reader. So now I call 

        dragonBones.animation.WorldClock.clock.advanceTime(-1);            

in my main game update function, and it works great, the anim speed seems to match up nicely with the preview in the tool - no need for a perhaps costly 20 ms event timer

Share this post


Link to post
Share on other sites

this is interesting.. I thought for a moment that there was an existing example of this with the pixi source on git.. but I was only half right.. there is a Spine example that uses a file called dragonbones.... how confusing :P

Share this post


Link to post
Share on other sites

if the advanceTime call gets passed a negative value, it implements the exact behavior with getTime that you left as an exercise for the reader

 

Oh ok cool, good find! For myself I always tend to have one central update loop running anyway though so I can just hook in to that.

 

@mrBRC - yes that spine example being called dragonBones had my brain briefly tied in a knot too!

Share this post


Link to post
Share on other sites

..and I have a theory why setting the rotation to skewX is supposed to work: if you rotate first, and then scale, that's skewing! Since apparently the phaser solution doesn't correctly scale at the moment (investigation pending :)) we only get the rotation

Share this post


Link to post
Share on other sites

Ok great, I hadn't looked back at the phaser one yet so I didn't realise it was something as simple as me forgetting to include scale! Thanks for that, I'll see if I can get an updated version of the example pushed back to the git repo.

 

I've just been doing some tests in the pixi version and I'm also a bit stumped re the skewing. Searching this forum there are a couple of posts about how to do skewing in Pixi as it is not supported by default.

 

http://www.html5gamedevs.com/topic/1300-how-to-use-transform-in-displayobject/?hl=skew#entry8924

 

and

 

http://www.html5gamedevs.com/topic/4745-transform-sprite-like-a-trapezoid-trapezium/?hl=skew#entry29141

 

I can set a modified updateTransform method for the sprites used in which I apply Math.tan to skewX and skewY properties but so far it doesn't produce the correct amount of skewing and also massively interferes with any rotation that should be going on.

Share this post


Link to post
Share on other sites

I think what's throwing me off is that if I update ONLY the worldtransform in updateTransfrom, and not .position, .rotation or .scale, nothing changes, not position, not scale, not rotation, and of course not skew ;) 

Share this post


Link to post
Share on other sites

okay, here's what I *think* we need to do:

 

1. set up the Phaser or PIXI dragonBones updateTransform to simply add the transform to _display as a property (or even just the skeX and skewY values)

2. extend displayObject.updateTransform to look for that property and if present, include skew (otherwise work the same way as normal)

 

I put a console.log(this); in PIXI's displayObject.updateTransform and the list included the displayBridge objects, so apparently they are updateTransformed twice! (with the second, in displayObect.updateTransform, apparently undoing what we did to it in the bridge, because it works from the existing non-worldtransform properties which do not include skew)

Share this post


Link to post
Share on other sites

OK, well here's what I am doing thus far, I'm working in the Pixi version.

 

PixiFactory.prototype._generateDisplay now generates a new display type: PIXI.SkewableSprite

 

here is the definition:

PIXI.SkewableSprite = function(tx){    PIXI.Sprite.call(this, tx);    this.skewX = 1;    this.skewY = 1;};PIXI.SkewableSprite.constructor = PIXI.SkewableSprite;PIXI.SkewableSprite.prototype = Object.create(PIXI.Sprite.prototype);PIXI.SkewableSprite.prototype.updateTransform = function(){    if(this.rotation !== this.rotationCache)    {        this.rotationCache = this.rotation;        this._sr =  Math.sin(this.rotation);        this._cr =  Math.cos(this.rotation);    }    var parentTransform = this.parent.worldTransform;;    var worldTransform = this.worldTransform;    var px = this.pivot.x;    var py = this.pivot.y;    var a00 = this._cr * this.scale.x,        //insert skewing here!        a01 = Math.tan(this.skewX),//-this._sr * this.scale.y,        a10 = Math.tan(this.skewY),//this._sr * this.scale.x,        a11 = this._cr * this.scale.y,        a02 = this.position.x - a00 * px - py * a01,        a12 = this.position.y - a11 * py - px * a10,        b00 = parentTransform.a, b01 = parentTransform.b,        b10 = parentTransform.c, b11 = parentTransform.d;    worldTransform.a = b00 * a00 + b01 * a10;    worldTransform.b = b00 * a01 + b01 * a11;    worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx;    worldTransform.c = b10 * a00 + b11 * a10;    worldTransform.d = b10 * a01 + b11 * a11;    worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty;    this.worldAlpha = this.alpha * this.parent.worldAlpha;};

It basically overrides the default updateTransform with a custom one that is almost identical except for two values that attempt to apply skewing.

 

The SkewableSprite definition also holds a skewX and skewY variable.

 

I've changed the PixiDisplayBridge updateTransform function to try to set the skewX and skewY values as follows:

PixiDisplayBridge.prototype.updateTransform = function (matrix, transform) {                this._display.x = matrix.tx;                this._display.y = matrix.ty;                this._display.skewX = transform.skewX;                this._display.skewY = transform.skewY;                this._display.rotation = transform.getRotation();//skewX;                this._display.scale.x = transform.scaleX;                this._display.scale.y = transform.scaleY;            };

The result is that the display content does seem to skew somewhat, but not to the extent that it should do. So I think we're probably on the right track here. The problem is the knock on effect this code has on sprites that are rotated but not skewed, they now fly around all over the place!

 

I've been referring to this old senocular tutorial for matrix guidance

http://www.senocular.com/flash/tutorials/transformmatrix/

and stack overflow

http://stackoverflow.com/questions/673216/skew-matrix-algorithm

but I might have to call it a day soon for the time being - I'll probably be able to pick this up again next week though.

Share this post


Link to post
Share on other sites

Oof. Spent a whole day on it, but I cracked it. That was a bunch of trial and error!

 

Here's the extended sprite I use:

Phaser.SkewableSprite = function (game, x, y, name) {     Phaser.Sprite.call(this, game, x, y, name);     this.skewX = 0;    this.skewY = 0;    }; Phaser.SkewableSprite.prototype = Object.create(Phaser.Sprite.prototype);Phaser.SkewableSprite.prototype.constructor = Phaser.SkewableSprite; Phaser.SkewableSprite.prototype.updateTransform = function(){    var parentTransform = this.parent.worldTransform;    var worldTransform = this.worldTransform;     worldTransform.a = 1;    worldTransform.b = 0;    worldTransform.tx = this.position.x + parentTransform.tx;     worldTransform.c = 0;    worldTransform.d = 1;    worldTransform.ty = this.position.y + parentTransform.ty;    worldTransform.postMultiplyParameters(Math.cos(-this.skewY) * this.scale.x, Math.sin(-this.skewX), Math.sin(this.skewY),  Math.cos(this.skewX) * this.scale.y, 0, 0); };

I'm using Phaser and not PIXI, but that shouldn't matter for the implementation details. Also please note that this SkewableSprite cannot currently be rotated via SkewableSprite.rotation. You'd have to do it through skewX and skewY values! It should be easy to implement though, I just don't have the nerve for it today any more ;) Please also note that this currently only inherits its position from its parent! I added a new function to PIXI.Matrix for the skew postMultiply call, this is it:

PIXI.Matrix.prototype.postMultiplyParameters = function(a, b, c, d, tx, ty){    var a1 = this.a;    var b1 = this.b;    var c1 = this.c;    var d1 = this.d;     this.a  = a * a1 + b * c1;    this.b  = a * b1 + b * d1;    this.c  = c * a1 + d * c1;    this.d  = c * b1 + d * d1;    this.tx = tx * a1 + ty * c1 + this.tx;    this.ty = tx * b1 + ty * d1 + this.ty;        return this; }; 

That wasn't strictly necessary, but it made the code much more readable and testable.

 

Here's how phaser_dragonbones.js is changed:

            PhaserDisplayBridge.prototype.updateTransform = function (matrix, transform) {                 this._display.x = transform.x;                this._display.y = transform.y;                this._display.skewX = transform.skewX;                  this._display.skewY = transform.skewY;                  this._display.scale.x = transform.scaleX;                this._display.scale.y = transform.scaleY;                            };
            PhaserBonesFactory.prototype._generateDisplay = function (textureAtlas, frameName, pivotX, pivotY) {                //get reference to the image object                var imageRef = textureAtlas.image;                //fetch the id of the atlas image                var imgName = textureAtlas.atlasId;                //create a sprite                var image = new Phaser.SkewableSprite(dragonBones.game, 0, 0, imgName);                //set the sprite frame from the texture atlas                image.animations.loadFrameData(image.game.cache.getFrameData(imgName));                //and the frameName... (restoring the .png that was stripped earlier)                image.frameName = frameName + ".png";                //set anchor point                image.anchor.setTo(pivotX / image.width, pivotY / image.height);                return image            };

This all together accurately reproduces Flash's fucked-up Rotate/Skew as it leaves the dragonBones exporter. My test cases are all playing back perfectly in Phaser now. Time to sleep :)

 

Share this post


Link to post
Share on other sites

Well, turns out implementing parent rotation/skew and pivot stuff isn't easy after all :(

 

I can get the dragonbones elements to rotate at the same speed as their containing group, but, they all rotate around their own registration points (called "pivot" in dragonbones, but currently implemented with anchor in the bridge - maybe that's the problem?)

Share this post


Link to post
Share on other sites
Phaser.DragonBonesSprite.prototype.updateTransform = function(matrix, transform){    // var localTransform = this.localTransform//.toArray();    var parentTransform = this.parent.worldTransform;//.toArray();    var worldTransform = this.worldTransform;//.toArray();     var px = this.pivot.x;    var py = this.pivot.y;     var a00 = this.scale.x * Math.cos(this.rotation + this.skewY),        a01 = this.scale.y * Math.sin(-this.rotation - this.skewX),        a10 = this.scale.x * Math.sin(this.rotation + this.skewY),        a11 = this.scale.y * Math.cos(this.rotation + this.skewX),        a02 = this.position.x - a00 * px - py * a01,        a12 = this.position.y - a11 * py - px * a10,        b00 = parentTransform.a, b01 = parentTransform.b,        b10 = parentTransform.c, b11 = parentTransform.d;     worldTransform.a = b00 * a00 + b01 * a10;    worldTransform.b = b00 * a01 + b01 * a11;    worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx;     worldTransform.c = b10 * a00 + b11 * a10;    worldTransform.d = b10 * a01 + b11 * a11;    worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty;     this.worldAlpha = this.alpha * this.parent.worldAlpha;    };
            PhaserDisplayBridge.prototype.updateTransform = function (matrix, transform) {                 this._display.x = transform.x;                this._display.y = transform.y;                this._display.skewX = transform.skewX;                  this._display.skewY = transform.skewY;                  this._display.scale.x = transform.scaleX;                this._display.scale.y = transform.scaleY;                            };
            PhaserBonesFactory.prototype._generateDisplay = function (textureAtlas, frameName, pivotX, pivotY) {                //get reference to the image object                var imageRef = textureAtlas.image;                //fetch the id of the atlas image                var imgName = textureAtlas.atlasId;                //create a sprite                var image = new Phaser.DragonBonesSprite(dragonBones.game, 0, 0, imgName);                //set the sprite frame from the texture atlas                image.animations.loadFrameData(image.game.cache.getFrameData(imgName));                //and the frameName... (restoring the .png that was stripped earlier)                image.frameName = frameName + ".png";                //set anchor point                // image.anchor.setTo(pivotX / image.width, pivotY / image.height);                image.pivot.setTo(pivotX, pivotY);                return image            }; 

there, that should do it.

Share this post


Link to post
Share on other sites

This looks like real progress! I've copied this over to the pixi version though and while it definitely does seem to apply skewing correctly I am now getting some other weird behaviour. I ran the dragon walking animation with it and his head was zooming around the stage.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.