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

This commit is contained in:
NathanSweet 2013-10-10 18:48:54 +02:00
parent 7bd9eeeee6
commit 26832677cb
22 changed files with 757 additions and 327 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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));
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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 {

View File

@ -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];
}
}
}
}

View File

@ -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]);
}
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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.

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}