diff --git a/spine-haxe/example/src/flixelExamples/AnimationBoundExample.hx b/spine-haxe/example/src/flixelExamples/AnimationBoundExample.hx new file mode 100644 index 000000000..f8be8878f --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/AnimationBoundExample.hx @@ -0,0 +1,72 @@ +package flixelExamples; + + +import flixel.util.FlxColor; +import flixel.text.FlxText; +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class AnimationBoundExample extends FlxState { + var loadBinary = true; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new CloudPotExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/spineboy.atlas"), new FlixelTextureLoader("assets/spineboy.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/spineboy-pro.skel") : Assets.getText("assets/spineboy-pro.json"), atlas, .2); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSpriteClipping = new SkeletonSprite(data, animationStateData); + var animationClipping = skeletonSpriteClipping.state.setAnimationByName(0, "portal", true).animation; + skeletonSpriteClipping.update(0); + skeletonSpriteClipping.setBoundingBox(animationClipping, true); + skeletonSpriteClipping.screenCenter(); + skeletonSpriteClipping.x = FlxG.width / 4 - skeletonSpriteClipping.width / 2; + add(skeletonSpriteClipping); + var textClipping = new FlxText(); + textClipping.text = "Animation bound with clipping"; + textClipping.size = 12; + textClipping.x = skeletonSpriteClipping.x + skeletonSpriteClipping.width / 2 - textClipping.width / 2; + textClipping.y = skeletonSpriteClipping.y + skeletonSpriteClipping.height + 20; + textClipping.setBorderStyle(FlxTextBorderStyle.OUTLINE, FlxColor.RED, 2); + add(textClipping); + + var skeletonSpriteNoClipping = new SkeletonSprite(data, animationStateData); + var animationClipping = skeletonSpriteNoClipping.state.setAnimationByName(0, "portal", true).animation; + skeletonSpriteNoClipping.update(0); + skeletonSpriteNoClipping.setBoundingBox(animationClipping, false); + skeletonSpriteNoClipping.screenCenter(); + skeletonSpriteNoClipping.x = FlxG.width / 4 * 3 - skeletonSpriteClipping.width / 2 - 50; + add(skeletonSpriteNoClipping); + var textNoClipping = new FlxText(); + textNoClipping.text = "Animation bound without clipping"; + textNoClipping.size = 12; + textNoClipping.x = skeletonSpriteNoClipping.x + skeletonSpriteNoClipping.width / 2 - textNoClipping.width / 2; + textNoClipping.y = skeletonSpriteNoClipping.y + skeletonSpriteNoClipping.height + 20; + textNoClipping.setBorderStyle(FlxTextBorderStyle.OUTLINE, FlxColor.RED, 2); + add(textNoClipping); + + var textInstruction = new FlxText(); + textInstruction.text = "Red rectangle is the animation bound"; + textInstruction.size = 12; + textInstruction.screenCenter(); + textInstruction.y = textNoClipping.y + 40; + textInstruction.setBorderStyle(FlxTextBorderStyle.OUTLINE, FlxColor.RED, 2); + add(textInstruction); + + FlxG.debugger.drawDebug = true; + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/BasicExample.hx b/spine-haxe/example/src/flixelExamples/BasicExample.hx index b3d2e1fa4..cd8376332 100644 --- a/spine-haxe/example/src/flixelExamples/BasicExample.hx +++ b/spine-haxe/example/src/flixelExamples/BasicExample.hx @@ -54,21 +54,13 @@ class BasicExample extends FlxState { animationStateData.defaultMix = 0.25; skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); - skeletonSprite.setPosition( - .5 * FlxG.width - skeletonSprite.width / 2, - .5 * FlxG.height - skeletonSprite.height / 2 - ); - - skeletonSprite.state.setAnimationByName(0, "walk", true); - + var animation = skeletonSprite.state.setAnimationByName(0, "walk", true).animation; + skeletonSprite.setBoundingBox(animation); + skeletonSprite.screenCenter(); add(skeletonSprite); - // addText("Click anywhere for next scene"); - - // addEventListener(TouchEvent.TOUCH, onTouch); super.create(); - FlxG.debugger.track(skeletonSprite); trace("loaded"); } @@ -80,10 +72,10 @@ class BasicExample extends FlxState { if (FlxG.keys.anyPressed([LEFT])) { skeletonSprite.x -= 15; } - if (FlxG.keys.anyPressed([UP])) { + if (FlxG.keys.anyPressed([DOWN])) { skeletonSprite.y += 15; } - if (FlxG.keys.anyPressed([DOWN])) { + if (FlxG.keys.anyPressed([UP])) { skeletonSprite.y -= 15; } diff --git a/spine-haxe/example/src/flixelExamples/CelestialCircusExample.hx b/spine-haxe/example/src/flixelExamples/CelestialCircusExample.hx new file mode 100644 index 000000000..ac1a8a3f2 --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/CelestialCircusExample.hx @@ -0,0 +1,35 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class CelestialCircusExample extends FlxState { + var loadBinary = true; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new SnowglobeExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/celestial-circus.atlas"), new FlixelTextureLoader("assets/celestial-circus.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/celestial-circus-pro.skel") : Assets.getText("assets/celestial-circus-pro.json"), atlas, .15); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + skeletonSprite.screenCenter(); + skeletonSprite.state.setAnimationByName(0, "eyeblink-long", true); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/CloudPotExample.hx b/spine-haxe/example/src/flixelExamples/CloudPotExample.hx new file mode 100644 index 000000000..cce3e855b --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/CloudPotExample.hx @@ -0,0 +1,35 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class CloudPotExample extends FlxState { + var loadBinary = true; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new AnimationBoundExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/cloud-pot.atlas"), new FlixelTextureLoader("assets/cloud-pot.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/cloud-pot.skel") : Assets.getText("assets/cloud-pot.json"), atlas, .25); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + skeletonSprite.screenCenter(); + skeletonSprite.state.setAnimationByName(0, "playing-in-the-rain", true); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/FlixelState.hx b/spine-haxe/example/src/flixelExamples/FlixelState.hx index cf72dad43..2ae77dfbe 100644 --- a/spine-haxe/example/src/flixelExamples/FlixelState.hx +++ b/spine-haxe/example/src/flixelExamples/FlixelState.hx @@ -30,7 +30,8 @@ class FlixelState extends FlxState override public function create():Void { - FlxG.switchState(new MixAndMatchExample()); + // FlxG.switchState(new AnimationBoundExample()); + // FlxG.switchState(new MixAndMatchExample()); // FlxG.switchState(new SequenceExample()); // FlxG.switchState(new BasicExample()); diff --git a/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx b/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx index bd28a32d6..f9c34a1bf 100644 --- a/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx +++ b/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx @@ -18,7 +18,7 @@ class MixAndMatchExample extends FlxState { var skeletonSprite:SkeletonSprite; override public function create():Void { - var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new BasicExample())); + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new TankExample())); button.setPosition(FlxG.width * .75, FlxG.height / 10); add(button); @@ -44,20 +44,9 @@ class MixAndMatchExample extends FlxState { // customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")); // skeletonSprite.skeleton.skin = customSkin; - camera.zoom = .5; skeletonSprite.skeleton.skinName = "full-skins/girl"; + skeletonSprite.state.update(0); skeletonSprite.setBoundingBox(); - - skeletonSprite.afterUpdateWorldTransforms = s -> { - for(slot in s.skeleton.slots) { - if (slot.data.name != "hair-patch") { - // slot.attachment = null; - } - } - } - - // skeletonSprite.y -=300; - skeletonSprite.screenCenter(); // skeletonSprite.state.setAnimationByName(0, "dance", true); add(skeletonSprite); @@ -65,7 +54,7 @@ class MixAndMatchExample extends FlxState { // FlxG.debugger.visible = !FlxG.debugger.visible; // FlxG.debugger.track(skeletonSprite); // FlxG.debugger.track(camera); - FlxG.debugger.drawDebug = true; + // FlxG.debugger.drawDebug = true; super.create(); } diff --git a/spine-haxe/example/src/flixelExamples/SackExample.hx b/spine-haxe/example/src/flixelExamples/SackExample.hx new file mode 100644 index 000000000..e30cba6da --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/SackExample.hx @@ -0,0 +1,36 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class SackExample extends FlxState { + var loadBinary = false; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new CelestialCircusExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/sack.atlas"), new FlixelTextureLoader("assets/sack.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/sack-pro.skel") : Assets.getText("assets/sack-pro.json"), atlas, .25); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + skeletonSprite.screenCenter(); + skeletonSprite.x -= 100; + skeletonSprite.state.setAnimationByName(0, "cape-follow-example", true); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/SequenceExample.hx b/spine-haxe/example/src/flixelExamples/SequenceExample.hx index 066d673fc..070784189 100644 --- a/spine-haxe/example/src/flixelExamples/SequenceExample.hx +++ b/spine-haxe/example/src/flixelExamples/SequenceExample.hx @@ -26,12 +26,10 @@ class SequenceExample extends FlxState { animationStateData.defaultMix = 0.25; skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); - skeletonSprite.screenCenter(); - skeletonSprite.y -= 100; - skeletonSprite.state.setAnimationByName(0, "flying", true); - FlxG.debugger.visible = !FlxG.debugger.visible; - FlxG.debugger.track(skeletonSprite); + var animation = skeletonSprite.state.setAnimationByName(0, "flying", true).animation; + skeletonSprite.setBoundingBox(animation); + skeletonSprite.screenCenter(); add(skeletonSprite); super.create(); } diff --git a/spine-haxe/example/src/flixelExamples/SnowglobeExample.hx b/spine-haxe/example/src/flixelExamples/SnowglobeExample.hx new file mode 100644 index 000000000..4b6ca3fd3 --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/SnowglobeExample.hx @@ -0,0 +1,35 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class SnowglobeExample extends FlxState { + var loadBinary = false; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new CloudPotExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/snowglobe.atlas"), new FlixelTextureLoader("assets/snowglobe.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/snowglobe-pro.skel") : Assets.getText("assets/snowglobe-pro.json"), atlas, .125); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + skeletonSprite.screenCenter(); + skeletonSprite.state.setAnimationByName(0, "shake", true); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/TankExample.hx b/spine-haxe/example/src/flixelExamples/TankExample.hx new file mode 100644 index 000000000..d354b3c35 --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/TankExample.hx @@ -0,0 +1,36 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class TankExample extends FlxState { + var loadBinary = true; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new VineExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/tank.atlas"), new FlixelTextureLoader("assets/tank.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/tank-pro.skel") : Assets.getText("assets/tank-pro.json"), atlas, .125); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + var animation = skeletonSprite.state.setAnimationByName(0, "drive", true).animation; + skeletonSprite.setBoundingBox(animation); + skeletonSprite.screenCenter(); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/example/src/flixelExamples/VineExample.hx b/spine-haxe/example/src/flixelExamples/VineExample.hx new file mode 100644 index 000000000..8ecfaf1ca --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/VineExample.hx @@ -0,0 +1,36 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class VineExample extends FlxState { + var loadBinary = true; + + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new SackExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/vine.atlas"), new FlixelTextureLoader("assets/vine.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/vine-pro.skel") : Assets.getText("assets/vine-pro.json"), atlas, .4); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(data, animationStateData); + var animation = skeletonSprite.state.setAnimationByName(0, "grow", true).animation; + skeletonSprite.setBoundingBox(animation); + skeletonSprite.screenCenter(); + add(skeletonSprite); + + super.create(); + } +} diff --git a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx index 6f6f0d893..c7de9b65b 100644 --- a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx @@ -1,5 +1,8 @@ package spine.flixel; +import spine.animation.MixDirection; +import spine.animation.MixBlend; +import spine.animation.Animation; import spine.TextureRegion; import haxe.extern.EitherType; import spine.attachments.Attachment; @@ -59,16 +62,48 @@ class SkeletonSprite extends FlxObject setBoundingBox(); } - public function setBoundingBox() { - var bounds = skeleton.getBounds(); + public function setBoundingBox(?animation:Animation, ?clip:Bool = true) { + var bounds = animation == null ? skeleton.getBounds() : getAnimationBounds(animation, clip); + trace(bounds); if (bounds.width > 0 && bounds.height > 0) { width = bounds.width; height = bounds.height; - offsetX = bounds.width / 2; - offsetY = bounds.height; + offsetX = -bounds.x; + offsetY = -bounds.y; } } + public function getAnimationBounds(animation:Animation, clip:Bool = true): lime.math.Rectangle { + var clipper = clip ? SkeletonSprite.clipper : null; + skeleton.setToSetupPose(); + + var steps = 100, time = 0.; + var stepTime = animation.duration != 0 ? animation.duration / steps : 0; + var minX = 100000000., maxX = -100000000., minY = 100000000., maxY = -100000000.; + + var bounds = new lime.math.Rectangle(); + for (i in 0...steps) { + animation.apply(skeleton, time , time, false, [], 1, MixBlend.setup, MixDirection.mixIn); + skeleton.updateWorldTransform(Physics.update); + bounds = skeleton.getBounds(clipper); + + if (!Math.isNaN(bounds.x) && !Math.isNaN(bounds.y) && !Math.isNaN(bounds.width) && !Math.isNaN(bounds.height)) { + minX = Math.min(bounds.x, minX); + minY = Math.min(bounds.y, minY); + maxX = Math.max(bounds.right, maxX); + maxY = Math.max(bounds.bottom, maxY); + } else + trace("ERROR"); + + time += stepTime; + } + bounds.x = minX; + bounds.y = minY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + return bounds; + } + override public function destroy():Void { skeleton = null; @@ -167,41 +202,16 @@ class SkeletonSprite extends FlxObject if (mesh != null) { // cannot use directly mesh.color.setRGBFloat otherwise the setter won't be called and transfor color not set - // trace('${slot.data.name}'); - // trace(skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat); - // trace(skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat); - // trace(skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat); - // trace('${mesh.color}\n'); var _tmpColor:Int; - // _tmpColor = FlxColor.fromRGBFloat(1,1,1,1); - - _tmpColor = FlxColor.fromRGBFloat( skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat, skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat, skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat, 1 ); - - - // // if (slot.data.name == "hair-patch") { - // if (slot.data.name == "square2") { - // _tmpColor = FlxColor.fromRGBFloat( - // skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat, - // skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat, - // skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat, - // 1 - // ); - // // continue; - // // trace('${mesh.color.red} | ${mesh.color.green} | ${mesh.color.blue} | ${mesh.color.alpha}'); - // } else { - // // trace(slot.data.name); - // _tmpColor = FlxColor.fromRGBFloat(1,1,1,1); - // } - // trace('${slot.data.name}\t${mesh.color}'); - - mesh.color = _tmpColor; - mesh.alpha = skeleton.color.a * slot.color.a * attachmentColor.a * alpha; + // mesh color and alpha are bugged + // mesh.color = _tmpColor; + // mesh.alpha = skeleton.color.a * slot.color.a * attachmentColor.a * alpha; if (clipper.isClipping()) { clipper.clipTriangles(worldVertices, triangles, triangles.length, uvs);