فهرست منبع

[haxe] Add SkeletonSprite.getAnimationBounds() to get the animation bound. Add AnimationBoundExample.

Davide Tantillo 1 سال پیش
والد
کامیت
d860c3dbb4

+ 103 - 0
spine-haxe/example/src/AnimationBoundExample.hx

@@ -0,0 +1,103 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated July 28, 2023. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2023, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software or
+ * otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
+ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*****************************************************************************/
+
+import Scene.SceneManager;
+import openfl.utils.Assets;
+import spine.SkeletonData;
+import spine.Physics;
+import spine.animation.AnimationStateData;
+import spine.atlas.TextureAtlas;
+import spine.starling.SkeletonSprite;
+import spine.starling.StarlingTextureLoader;
+import starling.core.Starling;
+import starling.events.TouchEvent;
+import starling.events.TouchPhase;
+import starling.display.Quad;
+
+class AnimationBoundExample extends Scene {
+	var loadBinary = false;
+	var skeletonSpriteClipping: SkeletonSprite;
+	var skeletonSpriteNoClipping: SkeletonSprite;
+	public function load():Void {
+		background.color = 0x333333;
+		var scale = .2;
+
+		var atlas = new TextureAtlas(Assets.getText("assets/spineboy.atlas"), new StarlingTextureLoader("assets/spineboy.atlas"));
+		var skeletondata = SkeletonData.from(Assets.getText("assets/spineboy-pro.json"), atlas);
+
+		var animationStateDataClipping = new AnimationStateData(skeletondata);
+		animationStateDataClipping.defaultMix = 0.25;
+
+		skeletonSpriteClipping = new SkeletonSprite(skeletondata, animationStateDataClipping);
+		skeletonSpriteClipping.skeleton.updateWorldTransform(Physics.update);
+		
+		skeletonSpriteClipping.scale = scale;
+		skeletonSpriteClipping.x = Starling.current.stage.stageWidth / 3 * 2;
+		skeletonSpriteClipping.y = Starling.current.stage.stageHeight / 2;
+		
+		var animationClipping = skeletonSpriteClipping.state.setAnimationByName(0, "portal", true).animation;
+		var animationBoundClipping = skeletonSpriteClipping.getAnimationBounds(animationClipping, true);
+		var quad:Quad = new Quad(animationBoundClipping.width * scale, animationBoundClipping.height * scale, 0xc70000);
+        quad.x = skeletonSpriteClipping.x + animationBoundClipping.x * scale;
+        quad.y = skeletonSpriteClipping.y + animationBoundClipping.y * scale;
+		
+		var animationStateDataNoClipping = new AnimationStateData(skeletondata);
+		animationStateDataNoClipping.defaultMix = 0.25;
+		skeletonSpriteNoClipping = new SkeletonSprite(skeletondata, animationStateDataNoClipping);
+		skeletonSpriteNoClipping.skeleton.updateWorldTransform(Physics.update);
+		skeletonSpriteNoClipping.scale = scale;
+		skeletonSpriteNoClipping.x = Starling.current.stage.stageWidth / 3;
+		skeletonSpriteNoClipping.y = Starling.current.stage.stageHeight / 2;
+
+		var animationNoClipping = skeletonSpriteNoClipping.state.setAnimationByName(0, "portal", true).animation;
+		var animationBoundNoClipping = skeletonSpriteNoClipping.getAnimationBounds(animationNoClipping, false);
+		var quadNoClipping:Quad = new Quad(animationBoundNoClipping.width * scale, animationBoundNoClipping.height * scale, 0xc70000);
+        quadNoClipping.x = skeletonSpriteNoClipping.x + animationBoundNoClipping.x * scale;
+        quadNoClipping.y = skeletonSpriteNoClipping.y + animationBoundNoClipping.y * scale;
+
+		addChild(quad);
+		addChild(quadNoClipping);
+		addChild(skeletonSpriteClipping);		
+		addChild(skeletonSpriteNoClipping);
+		addText("Animation bound without clipping", 75, 350);
+		addText("Animation bound with clipping", 370, 350);
+		addText("Red area is the animation bound", 240, 400);
+
+		juggler.add(skeletonSpriteClipping);
+		juggler.add(skeletonSpriteNoClipping);
+		addEventListener(TouchEvent.TOUCH, onTouch);
+	}
+
+	public function onTouch(e:TouchEvent) {
+		var touch = e.getTouch(this);
+		if (touch != null && touch.phase == TouchPhase.ENDED) {
+			SceneManager.getInstance().switchScene(new BasicExample());
+		}
+	}
+}

+ 1 - 1
spine-haxe/example/src/CloudPotExample.hx

@@ -72,7 +72,7 @@ class CloudPotExample extends Scene {
 	public function onTouch(e:TouchEvent) {
 		var touch = e.getTouch(this);
 		if (touch != null && touch.phase == TouchPhase.ENDED) {
-			SceneManager.getInstance().switchScene(new BasicExample());
+			SceneManager.getInstance().switchScene(new AnimationBoundExample());
 		}
 	}
 }

+ 30 - 0
spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx

@@ -29,6 +29,7 @@
 
 package spine.starling;
 
+import spine.animation.Animation;
 import starling.animation.IAnimatable;
 import openfl.geom.Matrix;
 import openfl.geom.Point;
@@ -40,6 +41,8 @@ import spine.SkeletonData;
 import spine.Slot;
 import spine.animation.AnimationState;
 import spine.animation.AnimationStateData;
+import spine.animation.MixBlend;
+import spine.animation.MixDirection;
 import spine.attachments.Attachment;
 import spine.attachments.ClippingAttachment;
 import spine.attachments.MeshAttachment;
@@ -314,6 +317,33 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
 		return resultRect;
 	}
 
+	public function getAnimationBounds(animation:Animation, clip:Bool = true): 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 bound:lime.math.Rectangle;
+		for (i in 0...steps) {
+			animation.apply(_skeleton, time , time, false, [], 1, MixBlend.setup, MixDirection.mixIn);
+			_skeleton.updateWorldTransform(Physics.update);
+			bound = _skeleton.getBounds(clipper);
+
+			if (!Math.isNaN(bound.x) && !Math.isNaN(bound.y) && !Math.isNaN(bound.width) && !Math.isNaN(bound.height)) {
+				minX = Math.min(bound.x, minX);
+				minY = Math.min(bound.y, minY);
+				maxX = Math.max(bound.right, maxX);
+				maxY = Math.max(bound.bottom, maxY);
+			} else
+				trace("ERROR");
+
+			time += stepTime;
+		}
+		return new Rectangle(minX, minY, maxX - minX, maxY - minY);
+	}
+
 	public var skeleton(get, never):Skeleton;
 
 	private function get_skeleton():Skeleton {