Browse Source

spine-as3 keyable draw order, events, and new AnimationState.

NathanSweet 12 years ago
parent
commit
26832677cb

+ 22 - 7
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);

+ 0 - 188
spine-as3/spine-as3/src/spine/AnimationState.as

@@ -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.<QueueEntry> = new Vector.<QueueEntry>();
-
-	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;
-}

+ 57 - 0
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;
+	}
+}
+
+}

+ 57 - 0
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;
+	}
+}
+
+}

+ 24 - 3
spine-as3/spine-as3/src/spine/SkeletonData.as

@@ -40,6 +40,7 @@ public class SkeletonData {
 	public var slots:Vector.<SlotData> = new Vector.<SlotData>(); // Setup pose draw order.
 	public var skins:Vector.<Skin> = new Vector.<Skin>();
 	public var defaultSkin:Skin;
+	public var eventDatas:Vector.<EventData> = new Vector.<EventData>();
 	public var animations:Vector.<Animation> = new Vector.<Animation>();
 
 	// --- 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)

+ 68 - 0
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.<int> = null;
+				if (drawOrderMap["offsets"]) {
+					drawOrder = new Vector.<int>(slotCount);
+					for (var i:int = slotCount - 1; i >= 0; i--)
+						drawOrder[i] = -1;
+					var offsets:Object = drawOrderMap["offsets"];
+					var unchanged:Vector.<int> = new Vector.<int>(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));
 	}
 

+ 11 - 6
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.<Event>) : 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.<Event>, 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 {

+ 240 - 0
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.<TrackEntry> = new Vector.<TrackEntry>();
+	private var _events:Vector.<Event> = new Vector.<Event>();
+	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 "<none>";
+		return buffer;
+	}
+}
+
+}
+
+import spine.animation.Animation;
+
+class QueueEntry {
+	public var animation:Animation;
+	public var loop:Boolean;
+	public var delay:Number;
+}

+ 3 - 3
spine-as3/spine-as3/src/spine/AnimationStateData.as → 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;
 

+ 3 - 4
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.<Number> = new Vector.<Number>(); // time, ...
 	public var attachmentNames:Vector.<String> = new Vector.<String>();
 
 	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.<Event>, alpha:Number) : void {
 		if (time < frames[0])
 			return; // Time is before first frame.
 

+ 2 - 1
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.<Event>, alpha:Number) : void {
 		if (time < frames[0])
 			return; // Time is before first frame.
 

+ 3 - 4
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.<Number> = new Vector.<Number>(); // 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.<Event>, alpha:Number) : void {
 	}
 
 	public function get frameCount () : int {
-		return _frameCount;
+		return curves.length / 6;
 	}
 
 	public function setLinear (frameIndex:int) : void {

+ 82 - 0
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.<Number> = new Vector.<Number>(); // time, ...
+	public var drawOrders:Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
+
+	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.<int>) : void {
+		frames[frameIndex] = time;
+		drawOrders[frameIndex] = drawOrder;
+	}
+
+	public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector.<Event>, 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.<Slot> = skeleton.drawOrder;
+		var slots:Vector.<Slot> = skeleton.slots;
+		var drawOrderToSetupIndex:Vector.<int> = 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];
+		}
+	}
+}
+
+}

+ 84 - 0
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.<Number> = new Vector.<Number>(); // time, ...
+	public var events:Vector.<Event> = new Vector.<Event>();
+
+	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.<Event>, 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]);
+	}
+}
+
+}

+ 2 - 1
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.<Event>, alpha:Number) : void {
 		if (time < frames[0])
 			return; // Time is before first frame.
 

+ 2 - 1
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.<Event>, alpha:Number) : void {
 		if (time < frames[0])
 			return; // Time is before first frame.
 

+ 2 - 1
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.<Event>, alpha:Number) : void;
 }
 
 }

+ 52 - 0
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 ? "<none>" : animation.name;
+	}
+}
+
+}

+ 2 - 1
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.<Event>, alpha:Number) : void {
 		if (time < frames[0])
 			return; // Time is before first frame.
 

+ 7 - 47
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.<AnimationState> = new Vector.<AnimationState>();
+	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();
-	}
 }
 
 }

+ 23 - 8
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);
 		}
 	}
 }

+ 11 - 52
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.<AnimationState> = new Vector.<AnimationState>();
-
-	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);
 	}
 }