alex_h Posted July 2, 2014 Share Posted July 2, 2014 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 cosmic, xdiepx and Mefteg 3 Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 9, 2014 Share Posted July 9, 2014 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. Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 9, 2014 Author Share Posted July 9, 2014 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. Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 9, 2014 Share Posted July 9, 2014 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 Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 9, 2014 Author Share Posted July 9, 2014 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. Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 9, 2014 Share Posted July 9, 2014 I see that rotation is currently done with skewX? that seems wrong Hmm but that's the way it's done in dragonBones.js :/ Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 9, 2014 Author Share Posted July 9, 2014 As far as I can remember I think I took their createJS example as a guide. I was also surprised to find skewX controlling rotation but then ... it worked! Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 9, 2014 Share Posted July 9, 2014 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 Quote Link to comment Share on other sites More sharing options...
mrBRC Posted July 9, 2014 Share Posted July 9, 2014 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 Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 10, 2014 Author Share Posted July 10, 2014 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! Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 ..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 Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 10, 2014 Author Share Posted July 10, 2014 Sounds plausible to me. I'm hoping to have time to look into correcting the missing scale feature later today. Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 got it (for the scale): this._display.scale.x = transform.scaleX; this._display.scale.y = transform.scaleY;(just like in the pixi bridge) Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 Still no skewing though :/ Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 10, 2014 Author Share Posted July 10, 2014 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. Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 I'm at the exact same point as you there Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 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 Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 10, 2014 Share Posted July 10, 2014 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) Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 10, 2014 Author Share Posted July 10, 2014 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 guidancehttp://www.senocular.com/flash/tutorials/transformmatrix/and stack overflowhttp://stackoverflow.com/questions/673216/skew-matrix-algorithmbut 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. Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 11, 2014 Share Posted July 11, 2014 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 FJGuerrero 1 Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 11, 2014 Share Posted July 11, 2014 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?) Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 11, 2014 Author Share Posted July 11, 2014 Oh, yeah anchor is wrong, it should be pivot I think. Well done for getting this far mate! Quote Link to comment Share on other sites More sharing options...
Mat Groves Posted July 11, 2014 Share Posted July 11, 2014 This looks awesome btw! great job Quote Link to comment Share on other sites More sharing options...
wayfinder Posted July 13, 2014 Share Posted July 13, 2014 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. Quote Link to comment Share on other sites More sharing options...
alex_h Posted July 14, 2014 Author Share Posted July 14, 2014 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.