mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 09:46:02 +08:00
[haxe][starling] Replace bounds calculation with BoundsProvider system. Close #2915.
Replace direct bounds calculation with BoundsProvider interface for better performance and correctness. This enables filters to work properly and makes it easier to get the correct bounds (SkeletonSprite.bounds). BREAKING CHANGES: - Removed getAnimationBounds() method. Replace it with the appropriate BoundsProvider implementation based on your use case, or create your own. - hitTest() now uses the assigned BoundsProvider instead of direct calculation. For accurate hit testing, use CurrentPoseBoundsProvider and call calculateBounds() each frame or on click. New features: - Add BoundsProvider abstract class and AABBRectangleBoundsProvider, CurrentPoseBoundsProvider, SetupPoseBoundsProvider, SkinsAndAnimationBoundsProvider implementations - SkeletonSprite constructor now accepts a third optional parameter for BoundsProvider. SetupPoseBoundsProvider is used by default. - Added calculateBounds() to recalculate bounds on demand (useful with CurrentPoseBoundsProvider)
This commit is contained in:
parent
cf0cb56609
commit
196df9c386
@ -29,10 +29,11 @@
|
|||||||
|
|
||||||
package starlingExamples;
|
package starlingExamples;
|
||||||
|
|
||||||
|
import starling.filters.BlurFilter;
|
||||||
|
import spine.boundsprovider.SkinsAndAnimationBoundsProvider;
|
||||||
import starlingExamples.Scene.SceneManager;
|
import starlingExamples.Scene.SceneManager;
|
||||||
import openfl.utils.Assets;
|
import openfl.utils.Assets;
|
||||||
import spine.SkeletonData;
|
import spine.SkeletonData;
|
||||||
import spine.Physics;
|
|
||||||
import spine.animation.AnimationStateData;
|
import spine.animation.AnimationStateData;
|
||||||
import spine.atlas.TextureAtlas;
|
import spine.atlas.TextureAtlas;
|
||||||
import spine.starling.SkeletonSprite;
|
import spine.starling.SkeletonSprite;
|
||||||
@ -42,55 +43,56 @@ import starling.events.TouchEvent;
|
|||||||
import starling.events.TouchPhase;
|
import starling.events.TouchPhase;
|
||||||
import starling.display.Quad;
|
import starling.display.Quad;
|
||||||
|
|
||||||
class AnimationBoundExample extends Scene {
|
class BoundsProviderExample extends Scene {
|
||||||
var loadBinary = false;
|
var loadBinary = false;
|
||||||
var skeletonSpriteClipping:SkeletonSprite;
|
var skeletonSpriteClipping:SkeletonSprite;
|
||||||
var skeletonSpriteNoClipping:SkeletonSprite;
|
var skeletonSpriteNoClipping:SkeletonSprite;
|
||||||
|
var quad:Quad;
|
||||||
|
var quadNoClipping:Quad;
|
||||||
|
private var movement = new openfl.geom.Point();
|
||||||
|
|
||||||
public function load():Void {
|
public function load():Void {
|
||||||
background.color = 0x333333;
|
background.color = 0x333333;
|
||||||
var scale = .2;
|
var scale = .4;
|
||||||
|
|
||||||
var atlas = new TextureAtlas(Assets.getText("assets/spineboy.atlas"), new StarlingTextureLoader("assets/spineboy.atlas"));
|
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 skeletondata = SkeletonData.from(Assets.getText("assets/spineboy-pro.json"), atlas, .5);
|
||||||
|
|
||||||
var animationStateDataClipping = new AnimationStateData(skeletondata);
|
|
||||||
animationStateDataClipping.defaultMix = 0.25;
|
|
||||||
|
|
||||||
skeletonSpriteClipping = new SkeletonSprite(skeletondata, animationStateDataClipping);
|
|
||||||
skeletonSpriteClipping.skeleton.updateWorldTransform(Physics.update);
|
|
||||||
|
|
||||||
|
var stateDataClipping = new AnimationStateData(skeletondata);
|
||||||
|
skeletonSpriteClipping = new SkeletonSprite(skeletondata, stateDataClipping, new SkinsAndAnimationBoundsProvider("portal", null, null, false));
|
||||||
skeletonSpriteClipping.scale = scale;
|
skeletonSpriteClipping.scale = scale;
|
||||||
skeletonSpriteClipping.x = Starling.current.stage.stageWidth / 3 * 2;
|
skeletonSpriteClipping.x = Starling.current.stage.stageWidth / 4 * 3;
|
||||||
skeletonSpriteClipping.y = Starling.current.stage.stageHeight / 2;
|
skeletonSpriteClipping.y = Starling.current.stage.stageHeight / 2;
|
||||||
|
skeletonSpriteClipping.state.setAnimationByName(0, "portal", true);
|
||||||
|
skeletonSpriteClipping.filter = new BlurFilter();
|
||||||
|
|
||||||
var animationClipping = skeletonSpriteClipping.state.setAnimationByName(0, "portal", true).animation;
|
var bounds = skeletonSpriteClipping.bounds;
|
||||||
var animationBoundClipping = skeletonSpriteClipping.getAnimationBounds(animationClipping, true);
|
quad = new Quad(bounds.width, bounds.height, 0xc70000);
|
||||||
var quad:Quad = new Quad(animationBoundClipping.width * scale, animationBoundClipping.height * scale, 0xc70000);
|
quad.x = bounds.x;
|
||||||
quad.x = skeletonSpriteClipping.x + animationBoundClipping.x * scale;
|
quad.y = bounds.y;
|
||||||
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(quad);
|
||||||
addChild(quadNoClipping);
|
|
||||||
addChild(skeletonSpriteClipping);
|
addChild(skeletonSpriteClipping);
|
||||||
|
|
||||||
|
var stateDataNoClipping = new AnimationStateData(skeletondata);
|
||||||
|
skeletonSpriteNoClipping = new SkeletonSprite(skeletondata, stateDataNoClipping, new SkinsAndAnimationBoundsProvider("portal", null, null, true));
|
||||||
|
skeletonSpriteNoClipping.scale = scale;
|
||||||
|
skeletonSpriteNoClipping.x = Starling.current.stage.stageWidth / 4;
|
||||||
|
skeletonSpriteNoClipping.y = Starling.current.stage.stageHeight / 2;
|
||||||
|
skeletonSpriteNoClipping.state.setAnimationByName(0, "portal", true);
|
||||||
|
skeletonSpriteNoClipping.filter = new BlurFilter();
|
||||||
|
|
||||||
|
bounds = skeletonSpriteNoClipping.bounds;
|
||||||
|
quadNoClipping = new Quad(bounds.width, bounds.height, 0xc70000);
|
||||||
|
quadNoClipping.x = bounds.x;
|
||||||
|
quadNoClipping.y = bounds.y;
|
||||||
|
addChild(quadNoClipping);
|
||||||
addChild(skeletonSpriteNoClipping);
|
addChild(skeletonSpriteNoClipping);
|
||||||
addText("Animation bound without clipping", 75, 350);
|
|
||||||
addText("Animation bound with clipping", 370, 350);
|
addText("Bounds with clipping", 40, 350);
|
||||||
addText("Red area is the animation bound", 240, 400);
|
addText("Bounds without clipping", 400, 350);
|
||||||
|
addText("Bounds created with SkinsAndAnimationBoundsProvider", 240, 400);
|
||||||
|
addText("The blur filter shows also the correcntess of the bounds.", 240, 450);
|
||||||
|
addText("You can move the elements around to see the bounds is always correct.", 240, 500);
|
||||||
|
|
||||||
juggler.add(skeletonSpriteClipping);
|
juggler.add(skeletonSpriteClipping);
|
||||||
juggler.add(skeletonSpriteNoClipping);
|
juggler.add(skeletonSpriteNoClipping);
|
||||||
@ -98,9 +100,33 @@ class AnimationBoundExample extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onTouch(e:TouchEvent) {
|
public function onTouch(e:TouchEvent) {
|
||||||
var touch = e.getTouch(this);
|
var skeletonTouch = e.getTouch(skeletonSpriteClipping);
|
||||||
if (touch != null && touch.phase == TouchPhase.ENDED) {
|
var skeletonTouch2 = e.getTouch(skeletonSpriteNoClipping);
|
||||||
|
if (skeletonTouch != null) {
|
||||||
|
if (skeletonTouch.phase == TouchPhase.MOVED) {
|
||||||
|
skeletonTouch.getMovement(this, movement);
|
||||||
|
skeletonSpriteClipping.x += movement.x;
|
||||||
|
skeletonSpriteClipping.y += movement.y;
|
||||||
|
|
||||||
|
var sBounds = skeletonSpriteClipping.bounds;
|
||||||
|
quad.x = sBounds.x;
|
||||||
|
quad.y = sBounds.y;
|
||||||
|
}
|
||||||
|
} else if (skeletonTouch2 != null) {
|
||||||
|
if (skeletonTouch2.phase == TouchPhase.MOVED) {
|
||||||
|
skeletonTouch2.getMovement(this, movement);
|
||||||
|
skeletonSpriteNoClipping.x += movement.x;
|
||||||
|
skeletonSpriteNoClipping.y += movement.y;
|
||||||
|
|
||||||
|
var sBounds = skeletonSpriteNoClipping.bounds;
|
||||||
|
quadNoClipping.x = sBounds.x;
|
||||||
|
quadNoClipping.y = sBounds.y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var sceneTouch = e.getTouch(this);
|
||||||
|
if (sceneTouch != null && sceneTouch.phase == TouchPhase.ENDED) {
|
||||||
SceneManager.getInstance().switchScene(new ControlBonesExample());
|
SceneManager.getInstance().switchScene(new ControlBonesExample());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
package starlingExamples;
|
package starlingExamples;
|
||||||
|
|
||||||
import spine.BlendMode;
|
|
||||||
import starlingExamples.Scene.SceneManager;
|
import starlingExamples.Scene.SceneManager;
|
||||||
import openfl.utils.Assets;
|
import openfl.utils.Assets;
|
||||||
import spine.SkeletonData;
|
import spine.SkeletonData;
|
||||||
@ -73,7 +72,7 @@ class CloudPotExample extends Scene {
|
|||||||
public function onTouch(e:TouchEvent) {
|
public function onTouch(e:TouchEvent) {
|
||||||
var touch = e.getTouch(this);
|
var touch = e.getTouch(this);
|
||||||
if (touch != null && touch.phase == TouchPhase.ENDED) {
|
if (touch != null && touch.phase == TouchPhase.ENDED) {
|
||||||
SceneManager.getInstance().switchScene(new AnimationBoundExample());
|
SceneManager.getInstance().switchScene(new BoundsProviderExample());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package spine.boundsprovider;
|
||||||
|
|
||||||
|
import spine.animation.AnimationState;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsGameObject;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsRectangle;
|
||||||
|
|
||||||
|
/** A bounds provider that provides a fixed size given by the user. */
|
||||||
|
class AABBRectangleBoundsProvider extends BoundsProvider {
|
||||||
|
private var x:Float;
|
||||||
|
private var y:Float;
|
||||||
|
private var width:Float;
|
||||||
|
private var height:Float;
|
||||||
|
|
||||||
|
public function new(x:Float, y:Float, width:Float, height:Float) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateBounds(gameObject:BoundsGameObject, out:BoundsRectangle):BoundsRectangle {
|
||||||
|
out.x = x;
|
||||||
|
out.y = y;
|
||||||
|
out.width = width;
|
||||||
|
out.height = height;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
spine-haxe/spine-haxe/spine/BoundsProvider/BoundsProvider.hx
Normal file
58
spine-haxe/spine-haxe/spine/BoundsProvider/BoundsProvider.hx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package spine.boundsprovider;
|
||||||
|
|
||||||
|
import spine.animation.AnimationState;
|
||||||
|
|
||||||
|
/** A bounds provider calculates the bounding box for a skeleton, which is then assigned as the size of the SpineGameObject. */
|
||||||
|
abstract class BoundsProvider {
|
||||||
|
/** Returns the bounding box for the skeleton, in skeleton space. */
|
||||||
|
abstract public function calculateBounds(gameObject:BoundsGameObject, out:BoundsRectangle):BoundsRectangle;
|
||||||
|
|
||||||
|
private function zeroRectangle(rectangle:BoundsRectangle):BoundsRectangle {
|
||||||
|
rectangle.x = 0;
|
||||||
|
rectangle.y = 0;
|
||||||
|
rectangle.width = 0;
|
||||||
|
rectangle.height = 0;
|
||||||
|
return rectangle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef BoundsGameObject = {
|
||||||
|
var skeleton(default, null):Skeleton;
|
||||||
|
var state(default, null):AnimationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef BoundsRectangle = {
|
||||||
|
var x:Float;
|
||||||
|
var y:Float;
|
||||||
|
var width:Float;
|
||||||
|
var height:Float;
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package spine.boundsprovider;
|
||||||
|
|
||||||
|
import spine.animation.AnimationState;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsGameObject;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsRectangle;
|
||||||
|
|
||||||
|
/** A bounds provider that calculates the bounding box from the current pose it is invoked. */
|
||||||
|
class CurrentPoseBoundsProvider extends BoundsProvider {
|
||||||
|
private var clipping:Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param clipping If true, clipping attachments are used to compute the bounds. False, by default.
|
||||||
|
*/
|
||||||
|
public function new(clipping = false) {
|
||||||
|
this.clipping = clipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateBounds(gameObject:BoundsGameObject, out:BoundsRectangle):BoundsRectangle {
|
||||||
|
var skeleton = gameObject.skeleton;
|
||||||
|
if (skeleton == null) {
|
||||||
|
zeroRectangle(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newBounds = skeleton.getBounds(clipping ? new SkeletonClipping() : null);
|
||||||
|
if (newBounds.width == Math.NEGATIVE_INFINITY) {
|
||||||
|
zeroRectangle(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.x = newBounds.x;
|
||||||
|
out.y = newBounds.y;
|
||||||
|
out.width = newBounds.width;
|
||||||
|
out.height = newBounds.height;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package spine.boundsprovider;
|
||||||
|
|
||||||
|
import spine.animation.AnimationState;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsGameObject;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsRectangle;
|
||||||
|
|
||||||
|
/** A bounds provider that calculates the bounding box from the setup pose. */
|
||||||
|
class SetupPoseBoundsProvider extends BoundsProvider {
|
||||||
|
private var clipping:Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param clipping If true, clipping attachments are used to compute the bounds. False, by default.
|
||||||
|
*/
|
||||||
|
public function new(clipping = false) {
|
||||||
|
this.clipping = clipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateBounds(gameObject:BoundsGameObject, out:BoundsRectangle):BoundsRectangle {
|
||||||
|
var skeleton = gameObject.skeleton;
|
||||||
|
if (skeleton == null) {
|
||||||
|
zeroRectangle(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy of skeleton as this might be called while
|
||||||
|
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||||
|
// reconstruct that state.
|
||||||
|
var skeleton = new Skeleton(skeleton.data);
|
||||||
|
skeleton.setupPose();
|
||||||
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
var newBounds = skeleton.getBounds(clipping ? new SkeletonClipping() : null);
|
||||||
|
if (newBounds.width == Math.NEGATIVE_INFINITY) {
|
||||||
|
zeroRectangle(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
out.x = newBounds.x;
|
||||||
|
out.y = newBounds.y;
|
||||||
|
out.width = newBounds.width;
|
||||||
|
out.height = newBounds.height;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package spine.boundsprovider;
|
||||||
|
|
||||||
|
import spine.animation.AnimationState;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsGameObject;
|
||||||
|
import spine.boundsprovider.BoundsProvider.BoundsRectangle;
|
||||||
|
|
||||||
|
/** A bounds provider that calculates the bounding box by taking the maximumg bounding box for a combination of skins and specific animation. */
|
||||||
|
class SkinsAndAnimationBoundsProvider extends BoundsProvider {
|
||||||
|
private var animation:String;
|
||||||
|
private var skins:Array<String>;
|
||||||
|
private var timeStep:Float;
|
||||||
|
private var clipping:Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param animation The animation to use for calculating the bounds. If null, the setup pose is used.
|
||||||
|
* @param skins The skins to use for calculating the bounds. If empty, the default skin is used.
|
||||||
|
* @param timeStep The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.
|
||||||
|
* @param clipping If true, clipping attachments are used to compute the bounds. False, by default.
|
||||||
|
*/
|
||||||
|
public function new(?animation:String, ?skins:Array<String>, timeStep:Float = 0.05, clipping = false) {
|
||||||
|
if (skins == null)
|
||||||
|
skins = [];
|
||||||
|
this.animation = animation;
|
||||||
|
this.skins = skins;
|
||||||
|
this.timeStep = timeStep;
|
||||||
|
this.clipping = clipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateBounds(gameObject:BoundsGameObject, out:BoundsRectangle):BoundsRectangle {
|
||||||
|
var skeleton = gameObject.skeleton;
|
||||||
|
var state = gameObject.state;
|
||||||
|
if (skeleton == null || state == null) {
|
||||||
|
zeroRectangle(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy of animation state and skeleton as this might be called while
|
||||||
|
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||||
|
// reconstruct that state.
|
||||||
|
var animationState = new AnimationState(state.data);
|
||||||
|
var skeleton = new Skeleton(skeleton.data);
|
||||||
|
var clipper = clipping ? new SkeletonClipping() : null;
|
||||||
|
var data = skeleton.data;
|
||||||
|
if (skins.length > 0) {
|
||||||
|
var customSkin = new Skin("custom-skin");
|
||||||
|
for (skinName in skins) {
|
||||||
|
var skin = data.findSkin(skinName);
|
||||||
|
|
||||||
|
if (skin == null)
|
||||||
|
continue;
|
||||||
|
customSkin.addSkin(skin);
|
||||||
|
}
|
||||||
|
skeleton.skin = customSkin;
|
||||||
|
}
|
||||||
|
skeleton.setupPose();
|
||||||
|
var animation = this.animation != null ? data.findAnimation(this.animation) : null;
|
||||||
|
|
||||||
|
if (animation == null) {
|
||||||
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
var newBounds = skeleton.getBounds(clipper);
|
||||||
|
out.x = newBounds.x;
|
||||||
|
out.y = newBounds.y;
|
||||||
|
out.width = newBounds.width;
|
||||||
|
out.height = newBounds.height;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minX = Math.POSITIVE_INFINITY,
|
||||||
|
minY = Math.POSITIVE_INFINITY,
|
||||||
|
maxX = Math.NEGATIVE_INFINITY,
|
||||||
|
maxY = Math.NEGATIVE_INFINITY;
|
||||||
|
animationState.clearTracks();
|
||||||
|
animationState.setAnimation(0, animation, false);
|
||||||
|
var steps = Math.max(animation.duration / this.timeStep, 1.0);
|
||||||
|
var i = 0.0;
|
||||||
|
while (i < steps) {
|
||||||
|
var delta = i > 0 ? this.timeStep : 0;
|
||||||
|
|
||||||
|
animationState.update(delta);
|
||||||
|
animationState.apply(skeleton);
|
||||||
|
skeleton.update(delta);
|
||||||
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
var bounds = skeleton.getBounds(clipper);
|
||||||
|
minX = Math.min(minX, bounds.x);
|
||||||
|
minY = Math.min(minY, bounds.y);
|
||||||
|
maxX = Math.max(maxX, bounds.x + bounds.width);
|
||||||
|
maxY = Math.max(maxY, bounds.y + bounds.height);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
out.x = minX;
|
||||||
|
out.y = minY;
|
||||||
|
out.width = maxX - minX;
|
||||||
|
out.height = maxY - minY;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
package spine.starling;
|
package spine.starling;
|
||||||
|
|
||||||
import spine.animation.Animation;
|
import spine.boundsprovider.BoundsProvider;
|
||||||
|
import spine.boundsprovider.SetupPoseBoundsProvider;
|
||||||
import starling.animation.IAnimatable;
|
import starling.animation.IAnimatable;
|
||||||
import openfl.geom.Matrix;
|
import openfl.geom.Matrix;
|
||||||
import openfl.geom.Point;
|
import openfl.geom.Point;
|
||||||
@ -42,8 +43,6 @@ import spine.SkeletonData;
|
|||||||
import spine.Slot;
|
import spine.Slot;
|
||||||
import spine.animation.AnimationState;
|
import spine.animation.AnimationState;
|
||||||
import spine.animation.AnimationStateData;
|
import spine.animation.AnimationStateData;
|
||||||
import spine.animation.MixBlend;
|
|
||||||
import spine.animation.MixDirection;
|
|
||||||
import spine.attachments.ClippingAttachment;
|
import spine.attachments.ClippingAttachment;
|
||||||
import spine.attachments.MeshAttachment;
|
import spine.attachments.MeshAttachment;
|
||||||
import spine.attachments.RegionAttachment;
|
import spine.attachments.RegionAttachment;
|
||||||
@ -55,7 +54,6 @@ import starling.rendering.VertexData;
|
|||||||
import starling.textures.Texture;
|
import starling.textures.Texture;
|
||||||
import starling.utils.Color;
|
import starling.utils.Color;
|
||||||
import starling.utils.MatrixUtil;
|
import starling.utils.MatrixUtil;
|
||||||
import starling.utils.Max;
|
|
||||||
|
|
||||||
/** A starling display object that draws a skeleton. */
|
/** A starling display object that draws a skeleton. */
|
||||||
class SkeletonSprite extends DisplayObject implements IAnimatable {
|
class SkeletonSprite extends DisplayObject implements IAnimatable {
|
||||||
@ -64,9 +62,13 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
|
|||||||
static private var _tempVertices:Array<Float> = new Array<Float>();
|
static private var _tempVertices:Array<Float> = new Array<Float>();
|
||||||
static private var blendModes:Array<String> = [BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN];
|
static private var blendModes:Array<String> = [BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN];
|
||||||
|
|
||||||
private var _skeleton:Skeleton;
|
public var skeleton(default, null):Skeleton;
|
||||||
|
public var state(default, null):AnimationState;
|
||||||
|
|
||||||
public var _state:AnimationState;
|
public var boundsProvider:BoundsProvider;
|
||||||
|
|
||||||
|
private var __bounds = new OpenFlRectangle();
|
||||||
|
private var _boundsPoint = [.0, .0];
|
||||||
|
|
||||||
private var _smoothing:String = "bilinear";
|
private var _smoothing:String = "bilinear";
|
||||||
|
|
||||||
@ -80,12 +82,14 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
|
|||||||
public var afterUpdateWorldTransforms:SkeletonSprite->Void = function(_) {};
|
public var afterUpdateWorldTransforms:SkeletonSprite->Void = function(_) {};
|
||||||
|
|
||||||
/** Creates an uninitialized SkeletonSprite. The skeleton and animation state must be set before use. */
|
/** Creates an uninitialized SkeletonSprite. The skeleton and animation state must be set before use. */
|
||||||
public function new(skeletonData:SkeletonData, animationStateData:AnimationStateData = null) {
|
public function new(skeletonData:SkeletonData, animationStateData:AnimationStateData = null, ?boundsProvider:BoundsProvider) {
|
||||||
super();
|
super();
|
||||||
Bone.yDown = true;
|
Bone.yDown = true;
|
||||||
_skeleton = new Skeleton(skeletonData);
|
skeleton = new Skeleton(skeletonData);
|
||||||
_skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
_state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData));
|
state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData));
|
||||||
|
this.boundsProvider = boundsProvider ?? new SetupPoseBoundsProvider();
|
||||||
|
this.calculateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function render(painter:Painter):Void {
|
override public function render(painter:Painter):Void {
|
||||||
@ -238,137 +242,37 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public function hitTest(localPoint:Point):DisplayObject {
|
override public function hitTest(localPoint:Point):DisplayObject {
|
||||||
if (!this.visible || !this.touchable)
|
if (!visible || !touchable)
|
||||||
return null;
|
return null;
|
||||||
|
else if (__bounds.containsPoint(localPoint))
|
||||||
var minX:Float = Max.MAX_VALUE;
|
|
||||||
var minY:Float = Max.MAX_VALUE;
|
|
||||||
var maxX:Float = -Max.MAX_VALUE;
|
|
||||||
var maxY:Float = -Max.MAX_VALUE;
|
|
||||||
var slots:Array<Slot> = skeleton.slots;
|
|
||||||
var worldVertices:Array<Float> = _tempVertices;
|
|
||||||
var empty:Bool = true;
|
|
||||||
for (i in 0...slots.length) {
|
|
||||||
var slot:Slot = slots[i];
|
|
||||||
var pose = slot.applied;
|
|
||||||
var attachment = pose.attachment;
|
|
||||||
if (attachment == null)
|
|
||||||
continue;
|
|
||||||
var verticesLength:Int;
|
|
||||||
if (Std.isOfType(attachment, RegionAttachment)) {
|
|
||||||
var region:RegionAttachment = cast(attachment, RegionAttachment);
|
|
||||||
verticesLength = 8;
|
|
||||||
region.computeWorldVertices(slot, worldVertices, 0, 2);
|
|
||||||
} else if (Std.isOfType(attachment, MeshAttachment)) {
|
|
||||||
var mesh:MeshAttachment = cast(attachment, MeshAttachment);
|
|
||||||
verticesLength = mesh.worldVerticesLength;
|
|
||||||
if (worldVertices.length < verticesLength)
|
|
||||||
worldVertices.resize(verticesLength);
|
|
||||||
mesh.computeWorldVertices(skeleton, slot, 0, verticesLength, worldVertices, 0, 2);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verticesLength != 0) {
|
|
||||||
empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ii:Int = 0;
|
|
||||||
while (ii < verticesLength) {
|
|
||||||
var x:Float = worldVertices[ii],
|
|
||||||
y:Float = worldVertices[ii + 1];
|
|
||||||
minX = minX < x ? minX : x;
|
|
||||||
minY = minY < y ? minY : y;
|
|
||||||
maxX = maxX > x ? maxX : x;
|
|
||||||
maxY = maxY > y ? maxY : y;
|
|
||||||
ii += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var temp:Float;
|
|
||||||
if (maxX < minX) {
|
|
||||||
temp = maxX;
|
|
||||||
maxX = minX;
|
|
||||||
minX = temp;
|
|
||||||
}
|
|
||||||
if (maxY < minY) {
|
|
||||||
temp = maxY;
|
|
||||||
maxY = minY;
|
|
||||||
minY = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localPoint.x >= minX && localPoint.x < maxX && localPoint.y >= minY && localPoint.y < maxY) {
|
|
||||||
return this;
|
return this;
|
||||||
}
|
else
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function getBounds(targetSpace:DisplayObject, resultRect:OpenFlRectangle = null):OpenFlRectangle {
|
public function calculateBounds() {
|
||||||
if (resultRect == null) {
|
this.boundsProvider.calculateBounds(this, __bounds);
|
||||||
resultRect = new OpenFlRectangle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public function getBounds(targetSpace:DisplayObject, out:OpenFlRectangle = null):OpenFlRectangle {
|
||||||
|
if (out == null)
|
||||||
|
out = new OpenFlRectangle();
|
||||||
|
|
||||||
if (targetSpace == this) {
|
if (targetSpace == this) {
|
||||||
resultRect.setTo(0, 0, 0, 0);
|
out.setTo(0, 0, __bounds.width, __bounds.height);
|
||||||
} else if (targetSpace == parent) {
|
} else if (targetSpace == parent) {
|
||||||
resultRect.setTo(x, y, 0, 0);
|
_boundsPoint[0] = __bounds.x;
|
||||||
|
_boundsPoint[1] = __bounds.y;
|
||||||
|
skeletonToHaxeWorldCoordinates(_boundsPoint);
|
||||||
|
out.setTo(_boundsPoint[0], _boundsPoint[1], __bounds.width * scaleX, __bounds.height * scaleX);
|
||||||
} else {
|
} else {
|
||||||
getTransformationMatrix(targetSpace, _tempMatrix);
|
getTransformationMatrix(targetSpace, _tempMatrix);
|
||||||
MatrixUtil.transformCoords(_tempMatrix, 0, 0, _tempPoint);
|
out.setTo(__bounds.x, __bounds.y, __bounds.width, __bounds.height);
|
||||||
resultRect.setTo(_tempPoint.x, _tempPoint.y, 0, 0);
|
MatrixUtil.transformCoords(_tempMatrix, out.x, out.y, _tempPoint);
|
||||||
}
|
out.setTo(_tempPoint.x, _tempPoint.y, out.width * scaleX, out.height * scaleY);
|
||||||
return resultRect;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAnimationBounds(animation:Animation, clip:Bool = true):Rectangle {
|
return out;
|
||||||
var clipper = clip ? SkeletonSprite.clipper : null;
|
|
||||||
_skeleton.setupPose();
|
|
||||||
|
|
||||||
var steps = 100, time = 0.;
|
|
||||||
var stepTime = animation.duration != 0 ? animation.duration / steps : 0;
|
|
||||||
var minX = 100000000.,
|
|
||||||
maxX = -100000000.,
|
|
||||||
minY = 100000000.,
|
|
||||||
maxY = -100000000.;
|
|
||||||
|
|
||||||
for (i in 0...steps) {
|
|
||||||
animation.apply(_skeleton, time, time, false, [], 1, MixBlend.setup, MixDirection.mixIn, false);
|
|
||||||
_skeleton.updateWorldTransform(Physics.update);
|
|
||||||
var boundsSkel = _skeleton.getBounds(clipper);
|
|
||||||
|
|
||||||
if (!Math.isNaN(boundsSkel.x) && !Math.isNaN(boundsSkel.y) && !Math.isNaN(boundsSkel.width) && !Math.isNaN(boundsSkel.height)) {
|
|
||||||
minX = Math.min(boundsSkel.x, minX);
|
|
||||||
minY = Math.min(boundsSkel.y, minY);
|
|
||||||
maxX = Math.max(boundsSkel.x + boundsSkel.width, maxX);
|
|
||||||
maxY = Math.max(boundsSkel.y + boundsSkel.height, maxY);
|
|
||||||
} else
|
|
||||||
throw new SpineException("Animation bounds are invalid: " + animation.name);
|
|
||||||
|
|
||||||
time += stepTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = new Rectangle();
|
|
||||||
bounds.x = minX;
|
|
||||||
bounds.y = minY;
|
|
||||||
bounds.width = maxX - minX;
|
|
||||||
bounds.height = maxY - minY;
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public var skeleton(get, never):Skeleton;
|
|
||||||
|
|
||||||
private function get_skeleton():Skeleton {
|
|
||||||
return _skeleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public var state(get, never):AnimationState;
|
|
||||||
|
|
||||||
private function get_state():AnimationState {
|
|
||||||
return _state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var smoothing(get, set):String;
|
public var smoothing(get, set):String;
|
||||||
@ -383,8 +287,8 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function advanceTime(time:Float):Void {
|
public function advanceTime(time:Float):Void {
|
||||||
_state.update(time);
|
state.update(time);
|
||||||
_state.apply(skeleton);
|
state.apply(skeleton);
|
||||||
this.beforeUpdateWorldTransforms(this);
|
this.beforeUpdateWorldTransforms(this);
|
||||||
skeleton.update(time);
|
skeleton.update(time);
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
@ -431,15 +335,14 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public function dispose():Void {
|
override public function dispose():Void {
|
||||||
if (_state != null) {
|
if (state != null) {
|
||||||
_state.clearListeners();
|
state.clearListeners();
|
||||||
_state = null;
|
state = null;
|
||||||
}
|
}
|
||||||
if (_skeleton != null)
|
if (skeleton != null)
|
||||||
_skeleton = null;
|
skeleton = null;
|
||||||
dispatchEventWith(starling.events.Event.REMOVE_FROM_JUGGLER);
|
dispatchEventWith(starling.events.Event.REMOVE_FROM_JUGGLER);
|
||||||
removeFromParent();
|
removeFromParent();
|
||||||
|
|
||||||
// this will remove also all starling event listeners
|
// this will remove also all starling event listeners
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user