diff --git a/spine-as3/spine-as3-example/src/Main.as b/spine-as3/spine-as3-example/src/Main.as index 912956a3f..6d6cd35f4 100644 --- a/spine-as3/spine-as3-example/src/Main.as +++ b/spine-as3/spine-as3-example/src/Main.as @@ -35,9 +35,10 @@ package { import flash.display.Sprite; -import spine.AnimationStateData; +import spine.Event; import spine.SkeletonData; import spine.SkeletonJson; +import spine.animation.AnimationStateData; import spine.atlas.Atlas; import spine.attachments.AtlasAttachmentLoader; import spine.flash.SingleTextureLoader; @@ -66,16 +67,30 @@ public class Main extends Sprite { stateData.setMixByName("jump", "walk", 0.4); stateData.setMixByName("jump", "jump", 0.2); - skeleton = new SkeletonAnimation(skeletonData); - skeleton.setAnimationStateData(stateData); + skeleton = new SkeletonAnimation(skeletonData, stateData); skeleton.x = 320; skeleton.y = 420; + + skeleton.state.onStart = function (trackIndex:int) : void { + trace(trackIndex + " start: " + skeleton.state.getCurrent(trackIndex)); + }; + skeleton.state.onEnd = function (trackIndex:int) : void { + trace(trackIndex + " end: " + skeleton.state.getCurrent(trackIndex)); + }; + skeleton.state.onComplete = function (trackIndex:int, count:int) : void { + trace(trackIndex + " complete: " + skeleton.state.getCurrent(trackIndex) + ", " + count); + }; + skeleton.state.onEvent = function (trackIndex:int, event:Event) : void { + trace(trackIndex + " event: " + skeleton.state.getCurrent(trackIndex) + ", " + + event.data.name + ": " + event.intValue + ", " + event.floatValue + ", " + event.stringValue); + }; + if (true) { - skeleton.setAnimation("drawOrder", true); + skeleton.state.setAnimationByName(0, "drawOrder", true); } else { - skeleton.setAnimation("walk", true); - skeleton.addAnimation("jump", false, 3); - skeleton.addAnimation("walk", true); + skeleton.state.setAnimationByName(0, "walk", true); + skeleton.state.addAnimationByName(0, "jump", false, 3); + skeleton.state.addAnimationByName(0, "walk", true, 0); } addChild(skeleton); diff --git a/spine-as3/spine-as3/src/spine/AnimationState.as b/spine-as3/spine-as3/src/spine/AnimationState.as deleted file mode 100644 index dfa14d623..000000000 --- a/spine-as3/spine-as3/src/spine/AnimationState.as +++ /dev/null @@ -1,188 +0,0 @@ -/****************************************************************************** - * Spine Runtime Software License - Version 1.1 - * - * Copyright (c) 2013, Esoteric Software - * All rights reserved. - * - * Redistribution and use in source and binary forms in whole or in part, with - * or without modification, are permitted provided that the following conditions - * are met: - * - * 1. A Spine Essential, Professional, Enterprise, or Education License must - * be purchased from Esoteric Software and the license must remain valid: - * http://esotericsoftware.com/ - * 2. Redistributions of source code must retain this license, which is the - * above copyright notice, this declaration of conditions and the following - * disclaimer. - * 3. Redistributions in binary form must reproduce this license, which is the - * above copyright notice, this declaration of conditions and the following - * disclaimer, in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -package spine { -import spine.animation.Animation; - -public class AnimationState { - private var _data:AnimationStateData; - private var current:Animation; - private var previous:Animation; - private var currentTime:Number; - private var previousTime:Number; - private var currentLoop:Boolean; - private var previousLoop:Boolean; - private var mixTime:Number; - private var mixDuration:Number; - private var queue:Vector. = new Vector.(); - - public function AnimationState (data:AnimationStateData) { - if (data == null) - throw new ArgumentError("data cannot be null."); - _data = data; - } - - public function update (delta:Number) : void { - currentTime += delta; - previousTime += delta; - mixTime += delta; - - if (queue.length > 0) { - var entry:QueueEntry = queue[0]; - if (currentTime >= entry.delay) { - setAnimationInternal(entry.animation, entry.loop); - queue.shift(); - } - } - } - - public function apply (skeleton:Skeleton) : void { - if (!current) - return; - if (previous) { - previous.apply(skeleton, previousTime, previousLoop); - var alpha:Number = mixTime / mixDuration; - if (alpha >= 1) { - alpha = 1; - previous = null; - } - current.mix(skeleton, currentTime, currentLoop, alpha); - } else - current.apply(skeleton, currentTime, currentLoop); - } - - public function clearAnimation () : void { - previous = null; - current = null; - clearQueue(); - } - - private function clearQueue () : void { - queue.length = 0; - } - - private function setAnimationInternal (animation:Animation, loop:Boolean) : void { - previous = null; - if (animation != null && current != null) { - mixDuration = _data.getMix(current, animation); - if (mixDuration > 0) { - mixTime = 0; - previous = current; - previousTime = currentTime; - previousLoop = currentLoop; - } - } - current = animation; - currentLoop = loop; - currentTime = 0; - } - - /** @see #setAnimation(Animation, Boolean) */ - public function setAnimationByName (animationName:String, loop:Boolean) : void { - var animation:Animation = _data.skeletonData.findAnimation(animationName); - if (animation == null) - throw new ArgumentError("Animation not found: " + animationName); - setAnimation(animation, loop); - } - - /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. - * @param animation May be null. */ - public function setAnimation (animation:Animation, loop:Boolean) : void { - clearQueue(); - setAnimationInternal(animation, loop); - } - - /** @see #addAnimation(Animation, Boolean, Number) */ - public function addAnimationByName (animationName:String, loop:Boolean, delay:Number) : void { - var animation:Animation = _data.skeletonData.findAnimation(animationName); - if (animation == null) - throw new ArgumentError("Animation not found: " + animationName); - addAnimation(animation, loop, delay); - } - - /** Adds an animation to be played delay seconds after the current or last queued animation. - * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ - public function addAnimation (animation:Animation, loop:Boolean, delay:Number) : void { - var entry:QueueEntry = new QueueEntry(); - entry.animation = animation; - entry.loop = loop; - - if (delay <= 0) { - var previousAnimation:Animation = queue.length == 0 ? current : queue[queue.length - 1].animation; - if (previousAnimation != null) - delay = previousAnimation.duration - _data.getMix(previousAnimation, animation) + delay; - else - delay = 0; - } - entry.delay = delay; - - queue.push(entry); - } - - /** @return May be null. */ - public function get animation () : Animation { - return current; - } - - /** Returns the time within the current animation. */ - public function get time () : Number { - return currentTime; - } - - public function set time (time:Number) : void { - currentTime = time; - } - - /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ - public function get isComplete () : Boolean { - return current == null || currentTime >= current.duration; - } - - public function get data () : AnimationStateData { - return _data; - } - - public function toString () : String { - return (current != null && current.name != null) ? current.name : super.toString(); - } -} - -} - -import spine.animation.Animation; - -class QueueEntry { - public var animation:Animation; - public var loop:Boolean; - public var delay:Number; -} diff --git a/spine-as3/spine-as3/src/spine/Event.as b/spine-as3/spine-as3/src/spine/Event.as new file mode 100644 index 000000000..14d0e353b --- /dev/null +++ b/spine-as3/spine-as3/src/spine/Event.as @@ -0,0 +1,57 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine { + +public class Event { + internal var _data:EventData; + public var intValue:int;; + public var floatValue:Number; + public var stringValue:String; + + public function Event (data:EventData) { + if (data == null) + throw new ArgumentError("data cannot be null."); + _data = data; + } + + public function get data () : EventData { + return _data; + } + + public function toString () : String { + return _data._name; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/EventData.as b/spine-as3/spine-as3/src/spine/EventData.as new file mode 100644 index 000000000..c5aac070f --- /dev/null +++ b/spine-as3/spine-as3/src/spine/EventData.as @@ -0,0 +1,57 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine { + +public class EventData { + internal var _name:String; + public var intValue:int;; + public var floatValue:Number; + public var stringValue:String; + + public function EventData (name:String) { + if (name == null) + throw new ArgumentError("name cannot be null."); + _name = name; + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return _name; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/SkeletonData.as b/spine-as3/spine-as3/src/spine/SkeletonData.as index 16ef7e460..ed3c7f257 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonData.as +++ b/spine-as3/spine-as3/src/spine/SkeletonData.as @@ -40,6 +40,7 @@ public class SkeletonData { public var slots:Vector. = new Vector.(); // Setup pose draw order. public var skins:Vector. = new Vector.(); public var defaultSkin:Skin; + public var eventDatas:Vector. = new Vector.(); public var animations:Vector. = new Vector.(); // --- Bones. @@ -119,15 +120,35 @@ public class SkeletonData { return skin; return null; } - + + // --- Events. + + public function addEvent (eventData:EventData) : void { + if (eventData == null) + throw new ArgumentError("eventData cannot be null."); + eventDatas.push(eventData); + } + + /** @return May be null. */ + public function findEvent (eventName:String) : EventData { + if (eventName == null) + throw new ArgumentError("eventName cannot be null."); + for (var i:int = 0, n:int = eventDatas.length; i < n; i++) { + var eventData:EventData = eventDatas[i]; + if (eventData.name == eventName) + return eventData; + } + return null; + } + // --- Animations. - + public function addAnimation (animation:Animation) : void { if (animation == null) throw new ArgumentError("animation cannot be null."); animations.push(animation); } - + /** @return May be null. */ public function findAnimation (animationName:String) : Animation { if (animationName == null) diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index ec7997f58..f80628a63 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -38,6 +38,8 @@ import spine.animation.Animation; import spine.animation.AttachmentTimeline; import spine.animation.ColorTimeline; import spine.animation.CurveTimeline; +import spine.animation.DrawOrderTimeline; +import spine.animation.EventTimeline; import spine.animation.RotateTimeline; import spine.animation.ScaleTimeline; import spine.animation.Timeline; @@ -142,6 +144,19 @@ public class SkeletonJson { skeletonData.defaultSkin = skin; } + // Events. + var events:Object = root["events"]; + if (events) { + for (var eventName:String in events) { + var eventMap:Object = events[eventName]; + var eventData:EventData = new EventData(eventName); + eventData.intValue = eventMap["int"] || 0; + eventData.floatValue = eventMap["float"] || 0; + eventData.stringValue = eventMap["string"] || null; + skeletonData.addEvent(eventData); + } + } + // Animations. var animations:Object = root["animations"]; for (var animationName:String in animations) @@ -265,6 +280,59 @@ public class SkeletonJson { } } + var eventsMap:Object = map["events"]; + if (eventsMap) { + var timeline4:EventTimeline = new EventTimeline(eventsMap.Count); + var frameIndex4:int = 0; + for each (var eventMap:Object in eventsMap) { + var eventData:EventData = skeletonData.findEvent(eventMap["name"]); + if (eventData == null) throw new Error("Event not found: " + eventMap["name"]); + var event:Event = new Event(eventData); + event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; + event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; + event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; + timeline4.setFrame(frameIndex4++, eventMap["time"], event); + } + timelines.push(timeline4); + duration = Math.max(duration, timeline.frames[timeline4.frameCount - 1]); + } + + var drawOrderValues:Object = map["draworder"]; + if (drawOrderValues) { + var timeline5:DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); + var slotCount:int = skeletonData.slots.length; + var frameIndex5:int = 0; + for each (var drawOrderMap:Object in drawOrderValues) { + var drawOrder:Vector. = null; + if (drawOrderMap["offsets"]) { + drawOrder = new Vector.(slotCount); + for (var i:int = slotCount - 1; i >= 0; i--) + drawOrder[i] = -1; + var offsets:Object = drawOrderMap["offsets"]; + var unchanged:Vector. = new Vector.(slotCount - offsets.length); + var originalIndex:int = 0, unchangedIndex:int = 0; + for each (var offsetMap:Object in offsets) { + var slotIndex2:int = skeletonData.findSlotIndex(offsetMap["slot"]); + if (slotIndex2 == -1) throw new Error("Slot not found: " + offsetMap["slot"]); + // Collect unchanged items. + while (originalIndex != slotIndex2) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (i = slotCount - 1; i >= 0; i--) + if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; + } + timeline5.setFrame(frameIndex5++, drawOrderMap["time"], drawOrder); + } + timelines.push(timeline5); + duration = Math.max(duration, timeline5.frames[timeline5.frameCount - 1]); + } + skeletonData.addAnimation(new Animation(name, timelines, duration)); } diff --git a/spine-as3/spine-as3/src/spine/animation/Animation.as b/spine-as3/spine-as3/src/spine/animation/Animation.as index 9a46b82f5..c102a8024 100644 --- a/spine-as3/spine-as3/src/spine/animation/Animation.as +++ b/spine-as3/spine-as3/src/spine/animation/Animation.as @@ -32,6 +32,7 @@ *****************************************************************************/ package spine.animation { +import spine.Event; import spine.Skeleton; public class Animation { @@ -54,28 +55,32 @@ public class Animation { } /** Poses the skeleton at the specified time for this animation. */ - public function apply (skeleton:Skeleton, time:Number, loop:Boolean) : void { + public function apply (skeleton:Skeleton, lastTime:Number, time:Number, loop:Boolean, events:Vector.) : void { if (skeleton == null) throw new ArgumentError("skeleton cannot be null."); - if (loop && duration != 0) + if (loop && duration != 0) { time %= duration; + lastTime %= duration; + } for (var i:int = 0, n:int = timelines.length; i < n; i++) - timelines[i].apply(skeleton, time, 1); + timelines[i].apply(skeleton, lastTime, time, events, 1); } /** Poses the skeleton at the specified time for this animation mixed with the current pose. * @param alpha The amount of this animation that affects the current pose. */ - public function mix (skeleton:Skeleton, time:Number, loop:Boolean, alpha:Number) : void { + public function mix (skeleton:Skeleton, lastTime:Number, time:Number, loop:Boolean, events:Vector., alpha:Number) : void { if (skeleton == null) throw new ArgumentError("skeleton cannot be null."); - if (loop && duration != 0) + if (loop && duration != 0) { time %= duration; + lastTime %= duration; + } for (var i:int = 0, n:int = timelines.length; i < n; i++) - timelines[i].apply(skeleton, time, alpha); + timelines[i].apply(skeleton, lastTime, time, events, alpha); } public function get name () : String { diff --git a/spine-as3/spine-as3/src/spine/animation/AnimationState.as b/spine-as3/spine-as3/src/spine/animation/AnimationState.as new file mode 100644 index 000000000..e06d11255 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/AnimationState.as @@ -0,0 +1,240 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine.animation { + +import spine.Event; +import spine.Skeleton; + +public class AnimationState { + private var _data:AnimationStateData; + private var _tracks:Vector. = new Vector.(); + private var _events:Vector. = new Vector.(); + public var onStart:Function, onEnd:Function, onComplete:Function, onEvent:Function; + public var timeScale:Number = 1; + + public function AnimationState (data:AnimationStateData) { + if (!data) throw new ArgumentError("data cannot be null."); + _data = data; + } + + public function update (delta:Number) : void { + delta *= timeScale; + for (var i:int = 0; i < _tracks.length; i++) { + var current:TrackEntry = _tracks[i]; + if (!current) continue; + + var trackDelta:Number = delta * current.timeScale; + var time:Number = current.time + trackDelta; + var endTime:Number = current.endTime; + + current.time = time; + if (current.previous) { + current.previous.time += trackDelta; + current.mixTime += trackDelta; + } + + // Check if completed the animation or a loop iteration. + if (current.loop ? (current.lastTime % endTime > time % endTime) : (current.lastTime < endTime && time >= endTime)) { + var count:int = (int)(time / endTime); + if (current.onComplete != null) current.onComplete(i, count); + if (onComplete != null) onComplete(i, count); + } + + var next:TrackEntry = current.next; + if (next) { + if (time - trackDelta > next.delay) setCurrent(i, next); + } else { + // End non-looping animation when it reaches its end time and there is no next entry. + if (!current.loop && current.lastTime >= current.endTime) clearTrack(i); + } + } + } + + public function apply (skeleton:Skeleton) : void { + for (var i:int = 0; i < _tracks.length; i++) { + var current:TrackEntry = _tracks[i]; + if (!current) continue; + + _events.length = 0; + + var time:Number = current.time; + var loop:Boolean = current.loop; + if (!loop && time > current.endTime) time = current.endTime; + + var previous:TrackEntry = current.previous; + if (!previous) + current.animation.apply(skeleton, current.lastTime, time, loop, _events); + else { + var previousTime:Number = previous.time; + if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; + previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); + + var alpha:Number = current.mixTime / current.mixDuration; + if (alpha >= 1) { + alpha = 1; + current.previous = null; + } + current.animation.mix(skeleton, current.lastTime, time, loop, _events, alpha); + } + + for each (var event:Event in _events) { + if (current.onEvent != null) current.onEvent(i, event); + if (onEvent != null) onEvent(i, event); + } + + current.lastTime = current.time; + } + } + + public function clearTracks () : void { + for (var i:int = 0, n:int = _tracks.length; i < n; i++) + clearTrack(i); + _tracks.length = 0; + } + + public function clearTrack (trackIndex:int) : void { + if (trackIndex >= _tracks.length) return; + var current:TrackEntry = _tracks[trackIndex]; + if (!current) return; + + if (current.onEnd != null) current.onEnd(trackIndex); + if (onEnd != null) onEnd(trackIndex); + + _tracks[trackIndex] = null; + } + + private function expandToIndex (index:int) : TrackEntry { + if (index < _tracks.length) return _tracks[index]; + while (index >= _tracks.length) + _tracks.push(null); + return null; + } + + private function setCurrent (index:int, entry:TrackEntry) : void { + var current:TrackEntry = expandToIndex(index); + if (current) { + current.previous = null; + + if (current.onEnd != null) current.onEnd(index); + if (onEnd != null) onEnd(index); + + entry.mixDuration = _data.getMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + entry.previous = current; + } + } + + _tracks[index] = entry; + + if (entry.onStart != null) entry.onStart(index); + if (onStart != null) onStart(index); + } + + public function setAnimationByName (trackIndex:int, animationName:String, loop:Boolean) : TrackEntry { + var animation:Animation = _data._skeletonData.findAnimation(animationName); + if (!animation) throw new ArgumentError("Animation not found: " + animationName); + return setAnimation(trackIndex, animation, loop); + } + + /** Set the current animation. Any queued animations are cleared. */ + public function setAnimation (trackIndex:int, animation:Animation, loop:Boolean) : TrackEntry { + var entry:TrackEntry = new TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + setCurrent(trackIndex, entry); + return entry; + } + + public function addAnimationByName (trackIndex:int, animationName:String, loop:Boolean, delay:Number) : TrackEntry { + var animation:Animation = _data._skeletonData.findAnimation(animationName); + if (!animation) throw new ArgumentError("Animation not found: " + animationName); + return addAnimation(trackIndex, animation, loop, delay); + } + + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + public function addAnimation (trackIndex:int, animation:Animation, loop:Boolean, delay:Number) : TrackEntry { + var entry:TrackEntry = new TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + + var last:TrackEntry = expandToIndex(trackIndex); + if (last) { + while (last.next) + last = last.next; + last.next = entry; + } else + _tracks[trackIndex] = entry; + + if (delay <= 0) { + if (last) + delay += last.endTime - _data.getMix(last.animation, animation); + else + delay = 0; + } + entry.delay = delay; + + return entry; + } + + /** May be null. */ + public function getCurrent (trackIndex:int) : TrackEntry { + if (trackIndex >= _tracks.length) return null; + return _tracks[trackIndex]; + } + + public function toString () : String { + var buffer:String = ""; + for each (var entry:TrackEntry in _tracks) { + if (!entry) continue; + if (buffer.length > 0) buffer += ", "; + buffer += entry.toString(); + } + if (buffer.length == 0) return ""; + return buffer; + } +} + +} + +import spine.animation.Animation; + +class QueueEntry { + public var animation:Animation; + public var loop:Boolean; + public var delay:Number; +} diff --git a/spine-as3/spine-as3/src/spine/AnimationStateData.as b/spine-as3/spine-as3/src/spine/animation/AnimationStateData.as similarity index 97% rename from spine-as3/spine-as3/src/spine/AnimationStateData.as rename to spine-as3/spine-as3/src/spine/animation/AnimationStateData.as index 34b3ff895..58167138f 100644 --- a/spine-as3/spine-as3/src/spine/AnimationStateData.as +++ b/spine-as3/spine-as3/src/spine/animation/AnimationStateData.as @@ -31,11 +31,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -package spine { -import spine.animation.Animation; +package spine.animation { +import spine.SkeletonData; public class AnimationStateData { - private var _skeletonData:SkeletonData; + internal var _skeletonData:SkeletonData; private var animationToMixTime:Object = new Object(); public var defaultMix:Number = 0; diff --git a/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as b/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as index d141b062f..f227f0967 100644 --- a/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as @@ -32,22 +32,21 @@ *****************************************************************************/ package spine.animation { +import spine.Event; import spine.Skeleton; public class AttachmentTimeline implements Timeline { public var slotIndex:int; - private var _frameCount:int; public var frames:Vector. = new Vector.(); // time, ... public var attachmentNames:Vector. = new Vector.(); public function AttachmentTimeline (frameCount:int) { - _frameCount = frameCount; frames.length = frameCount; attachmentNames.length = frameCount; } public function get frameCount () : int { - return _frameCount; + return frames.length; } /** Sets the time and value of the specified keyframe. */ @@ -56,7 +55,7 @@ public class AttachmentTimeline implements Timeline { attachmentNames[frameIndex] = attachmentName; } - public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as b/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as index b05e7ca78..1c6d3bba4 100644 --- a/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as @@ -32,6 +32,7 @@ *****************************************************************************/ package spine.animation { +import spine.Event; import spine.Skeleton; import spine.Slot; @@ -60,7 +61,7 @@ public class ColorTimeline extends CurveTimeline { frames[frameIndex + 4] = a; } - override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as index ae561f76e..c125a6681 100644 --- a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as @@ -32,6 +32,7 @@ *****************************************************************************/ package spine.animation { +import spine.Event; import spine.Skeleton; /** Base class for frames that use an interpolation bezier curve. */ @@ -41,18 +42,16 @@ public class CurveTimeline implements Timeline { static private const BEZIER_SEGMENTS:int = 10; private var curves:Vector. = new Vector.(); // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... - private var _frameCount:int; public function CurveTimeline (frameCount:int) { - _frameCount = frameCount; curves.length = frameCount * 6; } - public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { } public function get frameCount () : int { - return _frameCount; + return curves.length / 6; } public function setLinear (frameIndex:int) : void { diff --git a/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as new file mode 100644 index 000000000..ac2ad526d --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as @@ -0,0 +1,82 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine.animation { +import spine.Event; +import spine.Skeleton; +import spine.Slot; + +public class DrawOrderTimeline implements Timeline { + public var frames:Vector. = new Vector.(); // time, ... + public var drawOrders:Vector.> = new Vector.>(); + + public function DrawOrderTimeline (frameCount:int) { + frames.length = frameCount; + drawOrders.length = frameCount; + } + + public function get frameCount () : int { + return frames.length; + } + + /** Sets the time and value of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, drawOrder:Vector.) : void { + frames[frameIndex] = time; + drawOrders[frameIndex] = drawOrder; + } + + public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var frameIndex:int; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = Animation.binarySearch(frames, time, 1) - 1; + + var drawOrder:Vector. = skeleton.drawOrder; + var slots:Vector. = skeleton.slots; + var drawOrderToSetupIndex:Vector. = drawOrders[frameIndex]; + var i:int = 0; + if (drawOrderToSetupIndex == null) { + for each (var slot:Slot in skeleton.slots) + drawOrder[i++] = slot; + } else { + for each (var setupIndex:int in drawOrderToSetupIndex) + drawOrder[i++] = skeleton.slots[setupIndex]; + } + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as new file mode 100644 index 000000000..1f5103d29 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as @@ -0,0 +1,84 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine.animation { +import spine.Event; +import spine.Skeleton; +import spine.Slot; + +public class EventTimeline implements Timeline { + public var frames:Vector. = new Vector.(); // time, ... + public var events:Vector. = new Vector.(); + + public function EventTimeline (frameCount:int) { + frames.length = frameCount; + events.length = frameCount; + } + + public function get frameCount () : int { + return frames.length; + } + + /** Sets the time and value of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, event:Event) : void { + frames[frameIndex] = time; + events[frameIndex] = event; + } + + public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { + if (!firedEvents) return; + + if (lastTime >= frames[frameCount - 1]) return; // Last time is after last frame. + + if (lastTime > time) { // Fire events after last time for looped animations. + apply(skeleton, lastTime, int.MAX_VALUE, firedEvents, alpha); + lastTime = 0; + } + + var frameIndex:int; + if (lastTime <= frames[0] || frameCount == 1) + frameIndex = 0; + else { + frameIndex = Animation.binarySearch(frames, lastTime, 1); + var frame:Number = frames[frameIndex]; + while (frameIndex > 0) { // Fire multiple events with the same frame. + if (frames[frameIndex - 1] != frame) break; + frameIndex--; + } + } + for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) + firedEvents.push(events[frameIndex]); + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as index 230227048..eac7889d5 100644 --- a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as @@ -33,6 +33,7 @@ package spine.animation { import spine.Bone; +import spine.Event; import spine.Skeleton; public class RotateTimeline extends CurveTimeline { @@ -54,7 +55,7 @@ public class RotateTimeline extends CurveTimeline { frames[frameIndex + 1] = angle; } - override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as b/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as index 5a29d596d..059ec01ed 100644 --- a/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as @@ -33,6 +33,7 @@ package spine.animation { import spine.Bone; +import spine.Event; import spine.Skeleton; public class ScaleTimeline extends TranslateTimeline { @@ -40,7 +41,7 @@ public class ScaleTimeline extends TranslateTimeline { super(frameCount); } - override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/animation/Timeline.as b/spine-as3/spine-as3/src/spine/animation/Timeline.as index 0c7c6980b..74059ca8d 100644 --- a/spine-as3/spine-as3/src/spine/animation/Timeline.as +++ b/spine-as3/spine-as3/src/spine/animation/Timeline.as @@ -32,11 +32,12 @@ *****************************************************************************/ package spine.animation { +import spine.Event; import spine.Skeleton; public interface Timeline { /** Sets the value(s) for the specified time. */ - function apply (skeleton:Skeleton, time:Number, alpha:Number) : void; + function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void; } } diff --git a/spine-as3/spine-as3/src/spine/animation/TrackEntry.as b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as new file mode 100644 index 000000000..7a4ba9642 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine.animation { +import spine.Event; +import spine.Skeleton; + +public class TrackEntry { + public var next:TrackEntry; + internal var previous:TrackEntry; + public var animation:Animation; + public var loop:Boolean; + public var delay:Number, time:Number = 0, lastTime:Number = 0, endTime:Number, timeScale:Number = 1; + internal var mixTime:Number, mixDuration:Number; + public var onStart:Function, onEnd:Function, onComplete:Function, onEvent:Function; + + public function toString () : String { + return animation == null ? "" : animation.name; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as b/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as index 65f0737d5..982381c0e 100644 --- a/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as @@ -33,6 +33,7 @@ package spine.animation { import spine.Bone; +import spine.Event; import spine.Skeleton; public class TranslateTimeline extends CurveTimeline { @@ -56,7 +57,7 @@ public class TranslateTimeline extends CurveTimeline { frames[frameIndex + 2] = y; } - override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/flash/SkeletonAnimation.as b/spine-as3/spine-as3/src/spine/flash/SkeletonAnimation.as index e13e95f3d..9387795ec 100644 --- a/spine-as3/spine-as3/src/spine/flash/SkeletonAnimation.as +++ b/spine-as3/spine-as3/src/spine/flash/SkeletonAnimation.as @@ -32,64 +32,24 @@ *****************************************************************************/ package spine.flash { -import spine.AnimationState; -import spine.AnimationStateData; import spine.SkeletonData; +import spine.animation.AnimationState; +import spine.animation.AnimationStateData; public class SkeletonAnimation extends SkeletonSprite { - public var states:Vector. = new Vector.(); + public var state:AnimationState; - public function SkeletonAnimation (skeletonData:SkeletonData) { + public function SkeletonAnimation (skeletonData:SkeletonData, stateData:AnimationStateData = null) { super(skeletonData); - addAnimationState(); + state = new AnimationState(stateData ? stateData : new AnimationStateData(skeletonData)); } override public function advanceTime (time:Number) : void { - for each (var state:AnimationState in states) { - state.update(time); - state.apply(skeleton); - } + state.update(time); + state.apply(skeleton); skeleton.updateWorldTransform(); super.advanceTime(time); } - - public function addAnimationState (stateData:AnimationStateData = null) : void { - if (!stateData) - stateData = new AnimationStateData(skeleton.data); - states.push(new AnimationState(stateData)); - } - - public function setAnimationStateData (stateData:AnimationStateData, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - if (!stateData) - throw new ArgumentError("stateData cannot be null."); - states[stateIndex] = new AnimationState(stateData); - } - - public function setMix (fromAnimation:String, toAnimation:String, duration:Number, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].data.setMixByName(fromAnimation, toAnimation, duration); - } - - public function setAnimation (name:String, loop:Boolean, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].setAnimationByName(name, loop); - } - - public function addAnimation (name:String, loop:Boolean, delay:Number = 0, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].addAnimationByName(name, loop, delay); - } - - public function clearAnimation (stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].clearAnimation(); - } } } diff --git a/spine-starling/spine-starling-example/src/Game.as b/spine-starling/spine-starling-example/src/Game.as index cae0931a3..f2c93bc69 100644 --- a/spine-starling/spine-starling-example/src/Game.as +++ b/spine-starling/spine-starling-example/src/Game.as @@ -1,8 +1,9 @@ package { -import spine.AnimationStateData; +import spine.Event; import spine.SkeletonData; import spine.SkeletonJson; +import spine.animation.AnimationStateData; import spine.starling.SkeletonAnimation; import spine.starling.StarlingAtlasAttachmentLoader; @@ -39,13 +40,27 @@ public class Game extends Sprite { stateData.setMixByName("jump", "walk", 0.4); stateData.setMixByName("jump", "jump", 0.2); - skeleton = new SkeletonAnimation(skeletonData); - skeleton.setAnimationStateData(stateData); + skeleton = new SkeletonAnimation(skeletonData, stateData); skeleton.x = 320; skeleton.y = 420; - skeleton.setAnimation("walk", true); - skeleton.addAnimation("jump", false, 3); - skeleton.addAnimation("walk", true); + + skeleton.state.onStart = function (trackIndex:int) : void { + trace(trackIndex + " start: " + skeleton.state.getCurrent(trackIndex)); + }; + skeleton.state.onEnd = function (trackIndex:int) : void { + trace(trackIndex + " end: " + skeleton.state.getCurrent(trackIndex)); + }; + skeleton.state.onComplete = function (trackIndex:int, count:int) : void { + trace(trackIndex + " complete: " + skeleton.state.getCurrent(trackIndex) + ", " + count); + }; + skeleton.state.onEvent = function (trackIndex:int, event:Event) : void { + trace(trackIndex + " event: " + skeleton.state.getCurrent(trackIndex) + ", " + + event.data.name + ": " + event.intValue + ", " + event.floatValue + ", " + event.stringValue); + }; + + skeleton.state.setAnimationByName(0, "walk", true); + skeleton.state.addAnimationByName(0, "jump", false, 3); + skeleton.state.addAnimationByName(0, "walk", true, 0); addChild(skeleton); Starling.juggler.add(skeleton); @@ -56,8 +71,8 @@ public class Game extends Sprite { private function onClick (event:TouchEvent) : void { var touch:Touch = event.getTouch(this); if (touch && touch.phase == TouchPhase.BEGAN) { - skeleton.setAnimation("jump", false); - skeleton.addAnimation("walk", true); + skeleton.state.setAnimationByName(0, "jump", false); + skeleton.state.addAnimationByName(0, "walk", true, 0); } } } diff --git a/spine-starling/spine-starling/src/spine/starling/SkeletonAnimation.as b/spine-starling/spine-starling/src/spine/starling/SkeletonAnimation.as index 0bf5ede48..54c7102d6 100644 --- a/spine-starling/spine-starling/src/spine/starling/SkeletonAnimation.as +++ b/spine-starling/spine-starling/src/spine/starling/SkeletonAnimation.as @@ -32,64 +32,23 @@ *****************************************************************************/ package spine.starling { - import spine.AnimationState; - import spine.AnimationStateData; - import spine.SkeletonData; +import spine.animation.AnimationState; +import spine.animation.AnimationStateData; +import spine.SkeletonData; public class SkeletonAnimation extends SkeletonSprite { - public var states:Vector. = new Vector.(); - - public function SkeletonAnimation (skeletonData:SkeletonData) { + public var state:AnimationState; + + public function SkeletonAnimation (skeletonData:SkeletonData, stateData:AnimationStateData = null) { super(skeletonData); - addAnimationState(); + state = new AnimationState(stateData ? stateData : new AnimationStateData(skeletonData)); } - + override public function advanceTime (time:Number) : void { - super.advanceTime(time); - - for each (var state:AnimationState in states) { - state.update(time); - state.apply(skeleton); - } + state.update(time); + state.apply(skeleton); skeleton.updateWorldTransform(); - } - - public function addAnimationState (stateData:AnimationStateData = null) : void { - if (!stateData) - stateData = new AnimationStateData(skeleton.data); - states.push(new AnimationState(stateData)); - } - - public function setAnimationStateData (stateData:AnimationStateData, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - if (!stateData) - throw new ArgumentError("stateData cannot be null."); - states[stateIndex] = new AnimationState(stateData); - } - - public function setMix (fromAnimation:String, toAnimation:String, duration:Number, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].data.setMixByName(fromAnimation, toAnimation, duration); - } - - public function setAnimation (name:String, loop:Boolean, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].setAnimationByName(name, loop); - } - - public function addAnimation (name:String, loop:Boolean, delay:Number = 0, stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].addAnimationByName(name, loop, delay); - } - - public function clearAnimation (stateIndex:int = 0) : void { - if (stateIndex < 0 || stateIndex >= states.length) - throw new ArgumentError("stateIndex out of range."); - states[stateIndex].clearAnimation(); + super.advanceTime(time); } }