diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index b01cbf32e..630dcb098 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -70,7 +70,7 @@ namespace Spine { List timelines = this.timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, lastTime, time, 1, events); + timelines[i].Apply(skeleton, lastTime, time, events, 1); } /** @deprecated */ @@ -92,7 +92,7 @@ namespace Spine { List timelines = this.timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, lastTime, time, alpha, events); + timelines[i].Apply(skeleton, lastTime, time, events, alpha); } /** @param target After the first and before the last entry. */ @@ -120,7 +120,7 @@ namespace Spine { public interface Timeline { /** Sets the value(s) for the specified time. */ - void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents); + void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha); } /** Base class for frames that use an interpolation bezier curve. */ @@ -136,7 +136,7 @@ namespace Spine { curves = new float[(frameCount - 1) * 6]; } - abstract public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents); + abstract public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha); public void SetLinear (int frameIndex) { curves[frameIndex * 6] = LINEAR; @@ -225,7 +225,7 @@ namespace Spine { frames[frameIndex + 1] = angle; } - override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -288,7 +288,7 @@ namespace Spine { frames[frameIndex + 2] = y; } - override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -318,7 +318,7 @@ namespace Spine { : base(frameCount) { } - override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -370,7 +370,7 @@ namespace Spine { frames[frameIndex + 4] = a; } - override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -434,7 +434,7 @@ namespace Spine { attachmentNames[frameIndex] = attachmentName; } - public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -469,7 +469,7 @@ namespace Spine { events[frameIndex] = e; } - public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -478,7 +478,7 @@ namespace Spine { if (lastTime > time) { // Fire events after last time for looped animations. - Apply(skeleton, lastTime, int.MaxValue, alpha, firedEvents); + Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha); lastTime = 0; } @@ -520,7 +520,7 @@ namespace Spine { drawOrders[frameIndex] = drawOrder; } - public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 5ed439f72..55ffb357e 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -33,32 +33,18 @@ using System; using System.Collections.Generic; +using System.Text; namespace Spine { public class AnimationState { private AnimationStateData data; - private QueuedAnimation current, previous; - private float mixTime, mixDuration; - private List queue = new List(); + private List tracks = new List(); private List events = new List(); public AnimationStateData Data { get { return data; } } - public List Queue { get { return queue; } } - public QueuedAnimation Current { get { return current; } } - public Animation Animation { - get { return current != null ? current.animation : null; } - } - public float Time { - get { return current != null ? current.time : 0; } - set { if (current != null) current.Time = value; } - } - public bool Loop { - get { return current != null ? current.loop : false; } - set { if (current != null) current.Loop = value; } - } - public event EventHandler Start; - public event EventHandler End; + public event EventHandler Start; + public event EventHandler End; public event EventHandler Event; public event EventHandler Complete; @@ -68,177 +54,218 @@ namespace Spine { } public void Update (float delta) { - QueuedAnimation current = this.current; - if (current == null) return; + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry current = tracks[i]; + if (current == null) continue; - float time = current.time; - float duration = current.endTime; + float time = current.time + delta; + float endTime = current.endTime; - current.time = time + delta; - if (previous != null) { - previous.time += delta; - mixTime += delta; - } + current.time = time; + if (current.previous != null) { + current.previous.time += delta; + current.mixTime += delta; + } - // Check if completed the animation or a loop iteration. - if (current.loop ? (current.lastTime % duration > time % duration) : (current.lastTime < duration && time >= duration)) { - int count = (int)(time / duration); - current.OnComplete(this, count); - if (Complete != null) Complete(this, new CompleteArgs(count)); - } + // Check if completed the animation or a loop iteration. + if (current.loop ? (current.lastTime % endTime > time % endTime) : (current.lastTime < endTime && time >= endTime)) { + int count = (int)(time / endTime); + current.OnComplete(this, i, count); + if (Complete != null) Complete(this, new CompleteArgs(i, count)); + } - if (queue.Count > 0) { - QueuedAnimation entry = queue[0]; - if (time >= entry.delay) { - if (entry.animation == null) - ClearAnimation(); - else { - SetAnimationEntry(entry); - queue.RemoveAt(0); - } + TrackEntry next = current.next; + if (next != null && time >= next.delay) { + if (next.animation == null) + Clear(i); + else + SetCurrent(i, next); } } } public void Apply (Skeleton skeleton) { - QueuedAnimation current = this.current; + List events = this.events; + + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry current = tracks[i]; + if (current == null) continue; + + events.Clear(); + + TrackEntry previous = current.previous; + if (previous == null) + current.animation.Apply(skeleton, current.lastTime, current.time, current.loop, events); + else { + previous.animation.Apply(skeleton, int.MaxValue, previous.time, previous.loop, null); + float alpha = current.mixTime / current.mixDuration; + if (alpha >= 1) { + alpha = 1; + current.previous = null; + } + current.animation.Mix(skeleton, current.lastTime, current.time, current.loop, events, alpha); + } + + for (int ii = 0, nn = events.Count; ii < nn; ii++) { + Event e = events[ii]; + current.OnEvent(this, i, e); + if (Event != null) Event(this, new EventTriggeredArgs(i, e)); + } + + current.lastTime = current.time; + } + } + + public void Clear () { + for (int i = 0, n = tracks.Count; i < n; i++) + Clear(i); + tracks.Clear(); + } + + public void Clear (int trackIndex) { + if (trackIndex >= tracks.Count) return; + TrackEntry current = tracks[trackIndex]; if (current == null) return; - List events = this.events; - events.Clear(); + current.OnEnd(this, trackIndex); + if (End != null) End(this, new StartEndArgs(trackIndex)); - QueuedAnimation previous = this.previous; - if (previous != null) { - previous.animation.Apply(skeleton, int.MaxValue, previous.time, previous.loop, null); - float alpha = mixTime / mixDuration; - if (alpha >= 1) { - alpha = 1; - this.previous = null; - } - current.animation.Mix(skeleton, current.lastTime, current.time, current.loop, events, alpha); - } else - current.animation.Apply(skeleton, current.lastTime, current.time, current.loop, events); - - foreach (Event e in events) { - current.OnEvent(this, e); - if (Event != null) Event(this, new EventTriggeredArgs(e)); - } - - current.lastTime = current.time; + tracks[trackIndex] = null; } - public void ClearAnimation () { - previous = null; - current = null; - queue.Clear(); + private TrackEntry ExpandToIndex (int index) { + if (index < tracks.Count) return tracks[index]; + while (index >= tracks.Count) + tracks.Add(null); + return null; } - private void SetAnimationEntry (QueuedAnimation entry) { - previous = null; - - QueuedAnimation current = this.current; + private void SetCurrent (int index, TrackEntry entry) { + TrackEntry current = ExpandToIndex(index); if (current != null) { - current.OnEnd(this); - if (End != null) End(this, EventArgs.Empty); + current.previous = null; - mixDuration = data.GetMix(current.animation, entry.animation); - if (mixDuration > 0) { - mixTime = 0; - previous = current; + current.OnEnd(this, index); + if (End != null) End(this, new StartEndArgs(index)); + + entry.mixDuration = data.GetMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + entry.previous = current; } } - this.current = entry; - entry.OnStart(this); - if (Start != null) Start(this, EventArgs.Empty); + tracks[index] = entry; + + entry.OnStart(this, index); + if (Start != null) Start(this, new StartEndArgs(index)); } - public QueuedAnimation SetAnimation (String animationName, bool loop) { + public TrackEntry SetAnimation (int trackIndex, String animationName, bool loop) { Animation animation = data.skeletonData.FindAnimation(animationName); if (animation == null) throw new ArgumentException("Animation not found: " + animationName); - return SetAnimation(animation, loop); + return SetAnimation(trackIndex, animation, loop); } /** Set the current animation. Any queued animations are cleared. */ - public QueuedAnimation SetAnimation (Animation animation, bool loop) { - queue.Clear(); - QueuedAnimation entry = new QueuedAnimation(); + public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) { + TrackEntry entry = new TrackEntry(); entry.animation = animation; entry.loop = loop; entry.time = 0; entry.endTime = animation.Duration; - SetAnimationEntry(entry); + SetCurrent(trackIndex, entry); return entry; } - public QueuedAnimation AddAnimation (String animationName, bool loop) { - return AddAnimation(animationName, loop, 0); - } - - public QueuedAnimation AddAnimation (String animationName, bool loop, float delay) { + public TrackEntry AddAnimation (int trackIndex, String animationName, bool loop, float delay) { Animation animation = data.skeletonData.FindAnimation(animationName); if (animation == null) throw new ArgumentException("Animation not found: " + animationName); - return AddAnimation(animation, loop, delay); - } - - public QueuedAnimation AddAnimation (Animation animation, bool loop) { - return AddAnimation(animation, loop, 0); + 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 QueuedAnimation AddAnimation (Animation animation, bool loop, float delay) { - QueuedAnimation entry = new QueuedAnimation(); + public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) { + TrackEntry entry = new TrackEntry(); entry.animation = animation; entry.loop = loop; entry.time = 0; entry.endTime = animation != null ? animation.Duration : 0; + TrackEntry last = ExpandToIndex(trackIndex); + if (last != null) { + while (last.next != null) + last = last.next; + last.next = entry; + } else + tracks[trackIndex] = entry; + if (delay <= 0) { - QueuedAnimation previousEntry = queue.Count > 0 ? queue[queue.Count - 1] : current; - if (previousEntry != null) { - delay += previousEntry.endTime; - if (animation != null) delay += -data.GetMix(previousEntry.animation, animation); + if (last != null) { + delay += last.endTime; + if (animation != null) delay += -data.GetMix(last.animation, animation); } else delay = 0; } entry.delay = delay; - queue.Add(entry); return entry; } - /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ - public bool IsComplete () { - return current == null || current.time >= current.endTime; + /** @return May be null. */ + public TrackEntry getTrackEntry (int trackIndex) { + if (trackIndex >= tracks.Count) return null; + return tracks[trackIndex]; } override public String ToString () { - if (current == null || current.animation == null) return ""; - return current.animation.Name; + StringBuilder buffer = new StringBuilder(); + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry entry = tracks[i]; + if (entry == null) continue; + if (buffer.Length > 0) buffer.Append(", "); + buffer.Append(entry.ToString()); + } + if (buffer.Length == 0) return ""; + return buffer.ToString(); } } public class EventTriggeredArgs : EventArgs { + public int TrackIndex { get; private set; } public Event Event { get; private set; } - public EventTriggeredArgs (Event e) { + public EventTriggeredArgs (int trackIndex, Event e) { + TrackIndex = trackIndex; Event = e; } } public class CompleteArgs : EventArgs { + public int TrackIndex { get; private set; } public int LoopCount { get; private set; } - public CompleteArgs (int loopCount) { + public CompleteArgs (int trackIndex, int loopCount) { + TrackIndex = trackIndex; LoopCount = loopCount; } } - public class QueuedAnimation { + public class StartEndArgs : EventArgs { + public int TrackIndex { get; private set; } + + public StartEndArgs (int trackIndex) { + TrackIndex = trackIndex; + } + } + + public class TrackEntry { + internal TrackEntry next, previous; internal Animation animation; internal bool loop; internal float delay, time, lastTime, endTime; + internal float mixTime, mixDuration; public Animation Animation { get { return animation; } } public bool Loop { get { return loop; } set { loop = value; } } @@ -253,25 +280,25 @@ namespace Spine { } } - public event EventHandler Start; - public event EventHandler End; + public event EventHandler Start; + public event EventHandler End; public event EventHandler Event; public event EventHandler Complete; - internal void OnStart (AnimationState state) { - if (Start != null) Start(state, EventArgs.Empty); + internal void OnStart (AnimationState state, int index) { + if (Start != null) Start(state, new StartEndArgs(index)); } - internal void OnEnd (AnimationState state) { - if (End != null) End(state, EventArgs.Empty); + internal void OnEnd (AnimationState state, int index) { + if (End != null) End(state, new StartEndArgs(index)); } - internal void OnEvent (AnimationState state, Event e) { - if (Event != null) Event(state, new EventTriggeredArgs(e)); + internal void OnEvent (AnimationState state, int index, Event e) { + if (Event != null) Event(state, new EventTriggeredArgs(index, e)); } - internal void OnComplete (AnimationState state, int loopCount) { - if (Complete != null) Complete(state, new CompleteArgs(loopCount)); + internal void OnComplete (AnimationState state, int index, int loopCount) { + if (Complete != null) Complete(state, new CompleteArgs(index, loopCount)); } } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index fdf3ee64f..3705528ab 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -81,7 +81,7 @@ public class Animation { Array timelines = this.timelines; for (int i = 0, n = timelines.size; i < n; i++) - timelines.get(i).apply(skeleton, lastTime, time, 1, events); + timelines.get(i).apply(skeleton, lastTime, time, events, 1); } /** @deprecated */ @@ -103,7 +103,7 @@ public class Animation { Array timelines = this.timelines; for (int i = 0, n = timelines.size; i < n; i++) - timelines.get(i).apply(skeleton, lastTime, time, alpha, events); + timelines.get(i).apply(skeleton, lastTime, time, events, alpha); } public String getName () { @@ -138,7 +138,7 @@ public class Animation { static public interface Timeline { /** Sets the value(s) for the specified time. */ - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events); + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha); } /** Base class for frames that use an interpolation bezier curve. */ @@ -263,7 +263,7 @@ public class Animation { frames[frameIndex + 1] = angle; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events) { + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -333,7 +333,7 @@ public class Animation { frames[frameIndex + 2] = y; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events) { + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -363,7 +363,7 @@ public class Animation { super(frameCount); } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events) { + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -426,7 +426,7 @@ public class Animation { frames[frameIndex + 4] = a; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events) { + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -499,7 +499,7 @@ public class Animation { attachmentNames[frameIndex] = attachmentName; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array events) { + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -542,7 +542,7 @@ public class Animation { events[frameIndex] = event; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array firedEvents) { + public void apply (Skeleton skeleton, float lastTime, float time, Array firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -551,7 +551,7 @@ public class Animation { if (lastTime > time) { // Fire events after last time for looped animations. - apply(skeleton, lastTime, Integer.MAX_VALUE, alpha, firedEvents); + apply(skeleton, lastTime, Integer.MAX_VALUE, firedEvents, alpha); lastTime = 0; } @@ -601,7 +601,7 @@ public class Animation { drawOrders[frameIndex] = drawOrder; } - public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array firedEvents) { + public void apply (Skeleton skeleton, float lastTime, float time, Array firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index 17955dd06..43c674c31 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -40,11 +40,9 @@ import com.badlogic.gdx.utils.Pools; /** Stores state for an animation and automatically mixes between animations. */ public class AnimationState { private final AnimationStateData data; - private QueuedAnimation current, previous; - private float mixTime, mixDuration; - private final Array queue = new Array(); - private final Array listeners = new Array(); + private Array tracks = new Array(); private final Array events = new Array(); + private final Array listeners = new Array(); public AnimationState (AnimationStateData data) { if (data == null) throw new IllegalArgumentException("data cannot be null."); @@ -52,202 +50,196 @@ public class AnimationState { } public void update (float delta) { - QueuedAnimation current = this.current; - if (current == null) return; + for (int i = 0, n = tracks.size; i < n; i++) { + TrackEntry current = tracks.get(i); + if (current == null) continue; - float time = current.time; - float duration = current.endTime; + float time = current.time + delta; + float endTime = current.endTime; - current.time = time + delta; - if (previous != null) { - previous.time += delta; - mixTime += delta; - } + current.time = time; + if (current.previous != null) { + current.previous.time += delta; + current.mixTime += delta; + } - // Check if completed the animation or a loop iteration. - if (current.loop ? (current.lastTime % duration > time % duration) : (current.lastTime < duration && time >= duration)) { - int count = (int)(time / duration); - if (current.listener != null) current.listener.complete(count); - for (int i = 0, n = listeners.size; i < n; i++) - listeners.get(i).complete(count); - } + // Check if completed the animation or a loop iteration. + if (current.loop ? (current.lastTime % endTime > time % endTime) : (current.lastTime < endTime && time >= endTime)) { + int count = (int)(time / endTime); + if (current.listener != null) current.listener.complete(i, count); + for (int ii = 0, nn = listeners.size; ii < nn; ii++) + listeners.get(ii).complete(i, count); + } - if (queue.size > 0) { - QueuedAnimation entry = queue.first(); - if (time >= entry.delay) { - if (entry.animation == null) - clearAnimation(); - else { - setAnimationEntry(entry); - queue.removeIndex(0); - } + TrackEntry next = current.next; + if (next != null && time >= next.delay) { + if (next.animation == null) + clear(i); + else + setCurrent(i, next); } } } public void apply (Skeleton skeleton) { - QueuedAnimation current = this.current; + Array events = this.events; + int listenerCount = listeners.size; + + for (int i = 0, n = tracks.size; i < n; i++) { + TrackEntry current = tracks.get(i); + if (current == null) continue; + + events.size = 0; + + TrackEntry previous = current.previous; + if (previous == null) + current.animation.apply(skeleton, current.lastTime, current.time, current.loop, events); + else { + previous.animation.apply(skeleton, Integer.MAX_VALUE, previous.time, previous.loop, null); + float alpha = current.mixTime / current.mixDuration; + if (alpha >= 1) { + alpha = 1; + Pools.free(previous); + current.previous = null; + } + current.animation.mix(skeleton, current.lastTime, current.time, current.loop, events, alpha); + } + + for (int ii = 0, nn = events.size; ii < nn; ii++) { + Event event = events.get(ii); + if (current.listener != null) current.listener.event(i, event); + for (int iii = 0; iii < listenerCount; iii++) + listeners.get(iii).event(i, event); + } + + current.lastTime = current.time; + } + } + + public void clear () { + for (int i = 0, n = tracks.size; i < n; i++) + clear(i); + tracks.clear(); + } + + public void clear (int trackIndex) { + if (trackIndex >= tracks.size) return; + TrackEntry current = tracks.get(trackIndex); if (current == null) return; - Array events = this.events; - events.size = 0; + if (current.listener != null) current.listener.end(trackIndex); + for (int i = 0, n = listeners.size; i < n; i++) + listeners.get(i).end(trackIndex); - QueuedAnimation previous = this.previous; - if (previous != null) { - previous.animation.apply(skeleton, Integer.MAX_VALUE, previous.time, previous.loop, null); - float alpha = mixTime / mixDuration; - if (alpha >= 1) { - alpha = 1; - Pools.free(previous); - this.previous = null; + tracks.set(trackIndex, null); + freeAll(current); + if (current.previous != null) Pools.free(current.previous); + } + + private void freeAll (TrackEntry entry) { + while (entry != null) { + TrackEntry next = entry.next; + Pools.free(entry); + entry = next; + } + } + + private TrackEntry expandToIndex (int index) { + if (index < tracks.size) return tracks.get(index); + tracks.ensureCapacity(index - tracks.size + 1); + tracks.size = index + 1; + return null; + } + + private void setCurrent (int index, TrackEntry entry) { + TrackEntry current = expandToIndex(index); + if (current != null) { + if (current.previous != null) { + Pools.free(current.previous); + current.previous = null; } - current.animation.mix(skeleton, current.lastTime, current.time, current.loop, events, alpha); - } else - current.animation.apply(skeleton, current.lastTime, current.time, current.loop, events); - int listenerCount = listeners.size; - for (int i = 0, n = events.size; i < n; i++) { - Event event = events.get(i); - if (current.listener != null) current.listener.event(event); - for (int ii = 0; ii < listenerCount; ii++) - listeners.get(ii).event(event); - } - - current.lastTime = current.time; - } - - public void clearAnimation () { - if (previous != null) { - Pools.free(previous); - previous = null; - } - if (current != null) { - Pools.free(current); - current = null; - } - clearQueue(); - } - - private void clearQueue () { - Pools.freeAll(queue); - queue.clear(); - } - - private void setAnimationEntry (QueuedAnimation entry) { - if (previous != null) { - Pools.free(previous); - previous = null; - } - - QueuedAnimation current = this.current; - if (current != null) { - if (current.listener != null) current.listener.end(); + if (current.listener != null) current.listener.end(index); for (int i = 0, n = listeners.size; i < n; i++) - listeners.get(i).end(); + listeners.get(i).end(index); - mixDuration = data.getMix(current.animation, entry.animation); - if (mixDuration > 0) { - mixTime = 0; - previous = current; + entry.mixDuration = data.getMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + entry.previous = current; } else Pools.free(current); } - this.current = entry; - if (entry != null && entry.listener != null) entry.listener.start(); + tracks.set(index, entry); + + if (entry.listener != null) entry.listener.start(index); for (int i = 0, n = listeners.size; i < n; i++) - listeners.get(i).start(); + listeners.get(i).start(index); } - /** @see #setAnimation(Animation, boolean) */ - public QueuedAnimation setAnimation (String animationName, boolean loop) { + /** @see #setAnimation(int, Animation, boolean) */ + public TrackEntry setAnimation (int trackIndex, String animationName, boolean loop) { Animation animation = data.getSkeletonData().findAnimation(animationName); if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName); - return setAnimation(animation, loop); + return setAnimation(trackIndex, animation, loop); } /** Set the current animation. Any queued animations are cleared. */ - public QueuedAnimation setAnimation (Animation animation, boolean loop) { - clearQueue(); + public TrackEntry setAnimation (int trackIndex, Animation animation, boolean loop) { + TrackEntry current = expandToIndex(trackIndex); + if (current != null) freeAll(current.next); - QueuedAnimation entry = Pools.obtain(QueuedAnimation.class); + TrackEntry entry = Pools.obtain(TrackEntry.class); entry.animation = animation; entry.loop = loop; entry.time = 0; entry.endTime = animation.getDuration(); - setAnimationEntry(entry); + setCurrent(trackIndex, entry); return entry; } - /** @see #addAnimation(Animation, boolean) */ - public QueuedAnimation addAnimation (String animationName, boolean loop) { - return addAnimation(animationName, loop, 0); - } - - /** @see #addAnimation(Animation, boolean, float) */ - public QueuedAnimation addAnimation (String animationName, boolean loop, float delay) { + /** {@link #addAnimation(int, Animation, boolean, float)} */ + public TrackEntry addAnimation (int trackIndex, String animationName, boolean loop, float delay) { Animation animation = data.getSkeletonData().findAnimation(animationName); if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName); - return addAnimation(animation, loop, delay); - } - - /** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix - * duration. - * @param animation May be null to queue clearing the AnimationState. */ - public QueuedAnimation addAnimation (Animation animation, boolean loop) { - return addAnimation(animation, loop, 0); + return addAnimation(trackIndex, animation, loop, delay); } /** Adds an animation to be played delay seconds after the current or last queued animation. * @param animation May be null to queue clearing the AnimationState. * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ - public QueuedAnimation addAnimation (Animation animation, boolean loop, float delay) { - QueuedAnimation entry = Pools.obtain(QueuedAnimation.class); + public TrackEntry addAnimation (int trackIndex, Animation animation, boolean loop, float delay) { + TrackEntry entry = Pools.obtain(TrackEntry.class); entry.animation = animation; entry.loop = loop; entry.time = 0; entry.endTime = animation != null ? animation.getDuration() : 0; + TrackEntry last = expandToIndex(trackIndex); + if (last != null) { + while (last.next != null) + last = last.next; + last.next = entry; + } else + tracks.set(trackIndex, entry); + if (delay <= 0) { - QueuedAnimation previousEntry = queue.size > 0 ? queue.peek() : current; - if (previousEntry != null) { - delay += previousEntry.endTime; - if (animation != null) delay += -data.getMix(previousEntry.animation, animation); + if (last != null) { + delay += last.endTime; + if (animation != null) delay += -data.getMix(last.animation, animation); } else delay = 0; } entry.delay = delay; - queue.add(entry); return entry; } - public Array getQueue () { - return queue; - } - /** @return May be null. */ - public QueuedAnimation getCurrent () { - return current; - } - - /** @return May be null. */ - public Animation getAnimation () { - return current != null ? current.animation : null; - } - - /** Returns the time within the current animation. */ - public float getTime () { - return current != null ? current.time : 0; - } - - public void setTime (float time) { - if (current != null) current.setTime(time); - } - - /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ - public boolean isComplete () { - return current == null || current.time >= current.endTime; + public TrackEntry getTrackEntry (int trackIndex) { + if (trackIndex >= tracks.size) return null; + return tracks.get(trackIndex); } /** Adds a listener to receive events for all animations. */ @@ -266,26 +258,39 @@ public class AnimationState { } public String toString () { - if (current == null || current.animation == null) return ""; - return current.animation.getName(); + StringBuilder buffer = new StringBuilder(64); + for (int i = 0, n = tracks.size; i < n; i++) { + TrackEntry entry = tracks.get(i); + if (entry == null) continue; + if (buffer.length() > 0) buffer.append(", "); + buffer.append(entry.toString()); + } + if (buffer.length() == 0) return ""; + return buffer.toString(); } - /** A queued animation. */ - static public class QueuedAnimation implements Poolable { + static public class TrackEntry implements Poolable { + TrackEntry next, previous; Animation animation; boolean loop; float delay, time, lastTime, endTime; AnimationStateListener listener; + float mixTime, mixDuration; public void reset () { animation = null; listener = null; + next = null; } public Animation getAnimation () { return animation; } + public void setAnimation (Animation animation) { + this.animation = animation; + } + public boolean getLoop () { return loop; } @@ -294,13 +299,20 @@ public class AnimationState { this.loop = loop; } + public float getDelay () { + return delay; + } + + public void setDelay (float delay) { + this.delay = delay; + } + public float getTime () { return time; } public void setTime (float time) { this.time = time; - if (lastTime < time) lastTime = time; } public float getEndTime () { @@ -319,41 +331,58 @@ public class AnimationState { this.listener = listener; } - public float getDelay () { - return delay; + public float getLastTime () { + return lastTime; } - public void setDelay (float delay) { - this.delay = delay; + public void setLastTime (float lastTime) { + this.lastTime = lastTime; + } + + public TrackEntry getNext () { + return next; + } + + public void setNext (TrackEntry next) { + this.next = next; + } + + /** Returns true if the current time is greater than the end time, regardless of looping. */ + public boolean isComplete () { + return time >= endTime; + } + + public String toString () { + return animation == null ? "" : animation.name; } } static public interface AnimationStateListener { /** Invoked when the current animation triggers an event. */ - public void event (Event event); + public void event (int trackIndex, Event event); /** Invoked when the current animation has completed. * @param loopCount The number of times the animation reached the end. */ - public void complete (int loopCount); + public void complete (int trackIndex, int loopCount); /** Invoked just after the current animation is set. */ - public void start (); + public void start (int trackIndex); /** Invoked just before the current animation is replaced. */ - public void end (); + public void end (int trackIndex); } static public abstract class AnimationStateAdapter implements AnimationStateListener { - public void event (Event event) { + public void event (int trackIndex, Event event) { } - public void complete (int loopCount) { + public void complete (int trackIndex, int loopCount) { } - public void start () { + public void start (int trackIndex) { } - public void end () { + public void end (int trackIndex) { } } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java index 21ce6c8d2..607a1df6e 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java @@ -48,8 +48,9 @@ public class SkeletonData { bones.clear(); slots.clear(); skins.clear(); - animations.clear(); defaultSkin = null; + eventDatas.clear(); + animations.clear(); } // --- Bones. diff --git a/spine-libgdx/test/com/esotericsoftware/spine/AnimationStateTest.java b/spine-libgdx/test/com/esotericsoftware/spine/AnimationStateTest.java index f98099a85..bbee02001 100644 --- a/spine-libgdx/test/com/esotericsoftware/spine/AnimationStateTest.java +++ b/spine-libgdx/test/com/esotericsoftware/spine/AnimationStateTest.java @@ -69,23 +69,23 @@ public class AnimationStateTest extends ApplicationAdapter { state = new AnimationState(stateData); state.addListener(new AnimationStateListener() { - public void event (Event event) { - System.out.println("Event: " + event.getData().getName()); + public void event (int trackIndex, Event event) { + System.out.println(trackIndex + " event: " + state.getTrackEntry(trackIndex) + ", " + event.getData().getName()); } - public void complete (int loopCount) { - System.out.println("Complete: " + state.getAnimation() + ", " + loopCount); + public void complete (int trackIndex, int loopCount) { + System.out.println(trackIndex + " complete: " + state.getTrackEntry(trackIndex) + ", " + loopCount); } - public void start () { - System.out.println("Start: " + state.getAnimation()); + public void start (int trackIndex) { + System.out.println(trackIndex + " start: " + state.getTrackEntry(trackIndex)); } - public void end () { - System.out.println("End: " + state.getAnimation()); + public void end (int trackIndex) { + System.out.println(trackIndex + " end: " + state.getTrackEntry(trackIndex)); } }); - state.setAnimation("walk", true); + state.setAnimation(0, "walk", true); skeleton = new Skeleton(skeletonData); skeleton.setX(250); @@ -99,8 +99,11 @@ public class AnimationStateTest extends ApplicationAdapter { } public boolean keyDown (int keycode) { - state.setAnimation("jump", false); - state.addAnimation("walk", true); +// state.setAnimation(1, "jump", false); +// state.addAnimation(1, (Animation)null, true, 0); + + state.setAnimation(0, "jump", false); + state.addAnimation(0, "walk", true, 0); return true; } }); diff --git a/spine-xna/example/src/ExampleGame.cs b/spine-xna/example/src/ExampleGame.cs index ef84c1b3f..62675dfcd 100644 --- a/spine-xna/example/src/ExampleGame.cs +++ b/spine-xna/example/src/ExampleGame.cs @@ -88,19 +88,19 @@ namespace Spine { state = new AnimationState(stateData); - if (false) { + if (true) { // Event handling for all animations. - state.Start += new EventHandler(Start); - state.End += new EventHandler(End); + state.Start += new EventHandler(Start); + state.End += new EventHandler(End); state.Complete += new EventHandler(Complete); state.Event += new EventHandler(Event); - state.SetAnimation("drawOrder", true); + state.SetAnimation(0, "drawOrder", true); } else { - state.SetAnimation("walk", false); - QueuedAnimation entry = state.AddAnimation("jump", false); - entry.End += new EventHandler(End); // Event handling for queued animations. - state.AddAnimation("walk", true); + state.SetAnimation(0, "walk", false); + TrackEntry entry = state.AddAnimation(0, "jump", false, 0); + entry.End += new EventHandler(End); // Event handling for queued animations. + state.AddAnimation(0, "walk", true, 0); } skeleton.X = 320; @@ -149,20 +149,20 @@ namespace Spine { base.Draw(gameTime); } - public void Start (object sender, EventArgs e) { - Console.WriteLine(state + ": start"); + public void Start (object sender, StartEndArgs e) { + Console.WriteLine(e.TrackIndex + " " + state.getTrackEntry(e.TrackIndex) + ": start"); } - public void End (object sender, EventArgs e) { - Console.WriteLine(state + ": end"); + public void End (object sender, StartEndArgs e) { + Console.WriteLine(e.TrackIndex + " " + state.getTrackEntry(e.TrackIndex) + ": end"); } public void Complete (object sender, CompleteArgs e) { - Console.WriteLine(state + ": complete " + e.LoopCount); + Console.WriteLine(e.TrackIndex + " " + state.getTrackEntry(e.TrackIndex) + ": complete " + e.LoopCount); } public void Event (object sender, EventTriggeredArgs e) { - Console.WriteLine(state + ": event " + e.Event); + Console.WriteLine(e.TrackIndex + " " + state.getTrackEntry(e.TrackIndex) + ": event " + e.Event); } } }