diff --git a/spine-csharp/spine-csharp_xna.csproj b/spine-csharp/spine-csharp_xna.csproj index 373273e0d..12513e295 100644 --- a/spine-csharp/spine-csharp_xna.csproj +++ b/spine-csharp/spine-csharp_xna.csproj @@ -105,6 +105,8 @@ + + diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index 01624fbe5..32433c108 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -48,27 +48,47 @@ namespace Spine { Duration = duration; } - /** Poses the skeleton at the specified time for this animation. */ + /** @deprecated */ public void Apply (Skeleton skeleton, float time, bool loop) { + Apply(skeleton, time, time, loop, null); + } + + /** Poses the skeleton at the specified time for this animation. + * @param lastTime The last time the animation was applied. Can be equal to time if events shouldn't be fired. + * @param events Any triggered events are added. May be null if lastTime is known to not cause any events to trigger. */ + public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, List events) { if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); - if (loop && Duration != 0) time %= Duration; + if (loop && Duration != 0) { + time %= Duration; + lastTime %= Duration; + } List timelines = Timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, time, 1); + timelines[i].Apply(skeleton, lastTime, time, 1, events); + } + + /** @deprecated */ + public void Mix (Skeleton skeleton, float time, bool loop, float alpha) { + Mix(skeleton, time, time, loop, null, alpha); } /** Poses the skeleton at the specified time for this animation mixed with the current pose. + * @param lastTime The last time the animation was applied. Can be equal to time if events shouldn't be fired. + * @param events Any triggered events are added. May be null if lastTime is known to not cause any events to trigger. * @param alpha The amount of this animation that affects the current pose. */ - public void Mix (Skeleton skeleton, float time, bool loop, float alpha) { + public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, List events, float alpha) { if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); - if (loop && Duration != 0) time %= Duration; + if (loop && Duration != 0) { + time %= Duration; + lastTime %= Duration; + } List timelines = Timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, time, alpha); + timelines[i].Apply(skeleton, lastTime, time, alpha, events); } /** @param target After the first and before the last entry. */ @@ -96,7 +116,7 @@ namespace Spine { public interface Timeline { /** Sets the value(s) for the specified time. */ - void Apply (Skeleton skeleton, float time, float alpha); + void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents); } /** Base class for frames that use an interpolation bezier curve. */ @@ -116,7 +136,7 @@ namespace Spine { curves = new float[(frameCount - 1) * 6]; } - abstract public void Apply (Skeleton skeleton, float time, float alpha); + abstract public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents); public void SetLinear (int frameIndex) { curves[frameIndex * 6] = LINEAR; @@ -202,7 +222,7 @@ namespace Spine { Frames[frameIndex + 1] = angle; } - override public void Apply (Skeleton skeleton, float time, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = Frames; if (time < frames[0]) return; // Time is before first frame. @@ -262,7 +282,7 @@ namespace Spine { Frames[frameIndex + 2] = y; } - override public void Apply (Skeleton skeleton, float time, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = Frames; if (time < frames[0]) return; // Time is before first frame. @@ -292,7 +312,7 @@ namespace Spine { : base(frameCount) { } - override public void Apply (Skeleton skeleton, float time, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = Frames; if (time < frames[0]) return; // Time is before first frame. @@ -341,7 +361,7 @@ namespace Spine { Frames[frameIndex + 4] = a; } - override public void Apply (Skeleton skeleton, float time, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = Frames; if (time < frames[0]) return; // Time is before first frame. @@ -405,7 +425,7 @@ namespace Spine { AttachmentNames[frameIndex] = attachmentName; } - public void Apply (Skeleton skeleton, float time, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = Frames; if (time < frames[0]) return; // Time is before first frame. @@ -421,6 +441,57 @@ namespace Spine { } } + public class EventTimeline : Timeline { + public float[] Frames { get; private set; } // time, ... + public Event[] Events { get; private set; } + public int FrameCount { + get { + return Frames.Length; + } + } + + public EventTimeline (int frameCount) { + Frames = new float[frameCount]; + Events = new Event[frameCount]; + } + + /** Sets the time and value of the specified keyframe. */ + public void setFrame (int frameIndex, float time, Event e) { + Frames[frameIndex] = time; + Events[frameIndex] = e; + } + + public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + int frameCount = frames.Length; + 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.MaxValue, alpha, firedEvents); + lastTime = 0; + } + + int frameIndex; + if (frameCount == 1) + frameIndex = 0; + else { + frameIndex = Animation.binarySearch(frames, lastTime, 1); + float frame = frames[frameIndex]; + while (frameIndex > 0) { + float lastFrame = frames[frameIndex - 1]; + // Fire multiple events with the same frame and events that occurred at lastTime. + if (lastFrame != frame && lastFrame != lastTime) break; + frameIndex--; + } + } + for (; frameIndex < frameCount && time > frames[frameIndex]; frameIndex++) + firedEvents.Add(Events[frameIndex]); + } + } + public class DrawOrderTimeline : Timeline { public float[] Frames { get; private set; } // time, ... public int[][] DrawOrders { get; private set; } @@ -441,7 +512,7 @@ namespace Spine { DrawOrders[frameIndex] = drawOrder; } - public void Apply (Skeleton skeleton, float time, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List firedEvents) { float[] frames = 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 1e52a77ac..b931b639c 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -38,28 +38,55 @@ namespace Spine { public class AnimationState { public AnimationStateData Data { get; private set; } public Animation Animation { get; private set; } - public float Time { get; set; } + + private float time; + public float Time { + get { return time; } + set { + time = value; + currentLastTime = value - 0.00001f; + } + } + + private float currentLastTime; public bool Loop { get; set; } private Animation previous; private float previousTime; private bool previousLoop; + private QueueEntry currentQueueEntry; private float mixTime, mixDuration; + private List events = new List(); private List queue = new List(); + public event EventHandler Start; + public event EventHandler End; + public event EventHandler Event; + public event EventHandler Complete; + public AnimationState (AnimationStateData data) { if (data == null) throw new ArgumentNullException("data cannot be null."); Data = data; } public void Update (float delta) { - Time += delta; + time += delta; previousTime += delta; mixTime += delta; + if (Animation != null) { + float duration = Animation.Duration; + if (Loop ? (currentLastTime % duration > time % duration) + : (currentLastTime < duration && time >= duration)) { + int count = (int)(time / duration); + if (currentQueueEntry != null) currentQueueEntry.OnComplete(this, count); + if (Complete != null) Complete(this, new CompleteArgs(count)); + } + } + if (queue.Count > 0) { QueueEntry entry = queue[0]; - if (Time >= entry.delay) { - SetAnimationInternal(entry.animation, entry.loop); + if (time >= entry.delay) { + SetAnimationInternal(entry.animation, entry.loop, entry); queue.RemoveAt(0); } } @@ -67,35 +94,94 @@ namespace Spine { public void Apply (Skeleton skeleton) { if (Animation == null) return; + + List events = this.events; + events.Clear(); + if (previous != null) { - previous.Apply(skeleton, previousTime, previousLoop); + previous.Apply(skeleton, int.MaxValue, previousTime, previousLoop, null); float alpha = mixTime / mixDuration; if (alpha >= 1) { alpha = 1; previous = null; } - Animation.Mix(skeleton, Time, Loop, alpha); + Animation.Mix(skeleton, currentLastTime, time, Loop, events, alpha); } else - Animation.Apply(skeleton, Time, Loop); + Animation.Apply(skeleton, currentLastTime, time, Loop, events); + + if (Event != null || currentQueueEntry != null) { + foreach (Event e in events) { + if (currentQueueEntry != null) currentQueueEntry.OnEvent(this, e); + if (Event != null) Event(this, new EventTriggeredArgs(e)); + } + } + + currentLastTime = time; } - public void AddAnimation (String animationName, bool loop) { - AddAnimation(animationName, loop, 0); + public void ClearAnimation () { + previous = null; + Animation = null; + queue.Clear(); } - public void AddAnimation (String animationName, bool loop, float delay) { + private void SetAnimationInternal (Animation animation, bool loop, QueueEntry entry) { + previous = null; + if (Animation != null) { + if (currentQueueEntry != null) currentQueueEntry.OnEnd(this); + if (End != null) End(this, EventArgs.Empty); + + if (animation != null) { + mixDuration = Data.GetMix(Animation, animation); + if (mixDuration > 0) { + mixTime = 0; + previous = Animation; + previousTime = time; + previousLoop = Loop; + } + } + } + Animation = animation; + Loop = loop; + time = 0; + currentLastTime = 0; + currentQueueEntry = entry; + + if (currentQueueEntry != null) currentQueueEntry.OnStart(this); + if (Start != null) Start(this, EventArgs.Empty); + } + + public void SetAnimation (String animationName, bool loop) { Animation animation = Data.SkeletonData.FindAnimation(animationName); if (animation == null) throw new ArgumentException("Animation not found: " + animationName); - AddAnimation(animation, loop, delay); + SetAnimation(animation, loop); } - public void AddAnimation (Animation animation, bool loop) { - AddAnimation(animation, loop, 0); + /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. + * @param animation May be null. + * @param listener May be null. */ + public void SetAnimation (Animation animation, bool loop) { + queue.Clear(); + SetAnimationInternal(animation, loop, null); + } + + public QueueEntry AddAnimation (String animationName, bool loop) { + return AddAnimation(animationName, loop, 0); + } + + public QueueEntry AddAnimation (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 QueueEntry AddAnimation (Animation animation, bool loop) { + return AddAnimation(animation, loop, 0); } /** 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 void AddAnimation (Animation animation, bool loop, float delay) { + public QueueEntry AddAnimation (Animation animation, bool loop, float delay) { QueueEntry entry = new QueueEntry(); entry.animation = animation; entry.loop = loop; @@ -110,54 +196,59 @@ namespace Spine { entry.delay = delay; queue.Add(entry); - } - - private void SetAnimationInternal (Animation animation, bool loop) { - previous = null; - if (animation != null && Animation != null) { - mixDuration = Data.GetMix(Animation, animation); - if (mixDuration > 0) { - mixTime = 0; - previous = Animation; - previousTime = Time; - previousLoop = Loop; - } - } - Animation = animation; - Loop = loop; - Time = 0; - } - - public void SetAnimation (String animationName, bool loop) { - Animation animation = Data.SkeletonData.FindAnimation(animationName); - if (animation == null) throw new ArgumentException("Animation not found: " + animationName); - SetAnimation(animation, loop); - } - - public void SetAnimation (Animation animation, bool loop) { - queue.Clear(); - SetAnimationInternal(animation, loop); - } - - public void ClearAnimation () { - previous = null; - Animation = null; - queue.Clear(); + 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 Animation == null || Time >= Animation.Duration; + return Animation == null || time >= Animation.Duration; } override public String ToString () { return (Animation != null && Animation.Name != null) ? Animation.Name : base.ToString(); } } -} -class QueueEntry { - public Spine.Animation animation; - public bool loop; - public float delay; + public class EventTriggeredArgs : EventArgs { + public Event Event { get; private set; } + + public EventTriggeredArgs (Event e) { + Event = e; + } + } + + public class CompleteArgs : EventArgs { + public int LoopCount { get; private set; } + + public CompleteArgs (int loopCount) { + LoopCount = loopCount; + } + } + + public class QueueEntry { + internal Spine.Animation animation; + internal bool loop; + internal float delay; + + 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 OnEnd (AnimationState state) { + if (End != null) End(state, EventArgs.Empty); + } + + internal void OnEvent (AnimationState state, Event e) { + if (Event != null) Event(state, new EventTriggeredArgs(e)); + } + + internal void OnComplete (AnimationState state, int loopCount) { + if (Complete != null) Complete(state, new CompleteArgs(loopCount)); + } + } } diff --git a/spine-csharp/src/Event.cs b/spine-csharp/src/Event.cs new file mode 100644 index 000000000..c789c3b9c --- /dev/null +++ b/spine-csharp/src/Event.cs @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * 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 Single User License or Spine Professional 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. + *****************************************************************************/ + +using System; + +namespace Spine { + public class Event { + public EventData Data { get; private set; } + public int Int { get; set; } + public float Float { get; set; } + public String String { get; set; } + + public Event (EventData data) { + Data = data; + } + + override public String ToString () { + return Data.Name; + } + } +} diff --git a/spine-csharp/src/EventData.cs b/spine-csharp/src/EventData.cs new file mode 100644 index 000000000..b835f47a7 --- /dev/null +++ b/spine-csharp/src/EventData.cs @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * 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 Single User License or Spine Professional 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. + *****************************************************************************/ + +using System; + +namespace Spine { + public class EventData { + public String Name { get; private set; } + public int Int { get; set; } + public float Float { get; set; } + public String String { get; set; } + + public EventData (String name) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + Name = name; + } + + override public String ToString () { + return Name; + } + } +} diff --git a/spine-csharp/src/SkeletonBounds.cs b/spine-csharp/src/SkeletonBounds.cs index 468a4b085..fbbf68e95 100644 --- a/spine-csharp/src/SkeletonBounds.cs +++ b/spine-csharp/src/SkeletonBounds.cs @@ -133,8 +133,10 @@ namespace Spine { } else polygon = new Polygon(); polygons.Add(polygon); - polygon.Count = boundingBox.Vertices.Length; - if (polygon.Vertices.Length < polygon.Count) polygon.Vertices = new float[polygon.Count]; + + int count = boundingBox.Vertices.Length; + polygon.Count = count; + if (polygon.Vertices.Length < count) polygon.Vertices = new float[count]; boundingBox.ComputeWorldVertices(x, y, slot.Bone, polygon.Vertices); } } diff --git a/spine-csharp/src/SkeletonData.cs b/spine-csharp/src/SkeletonData.cs index 6d2f308b1..4aa079e03 100644 --- a/spine-csharp/src/SkeletonData.cs +++ b/spine-csharp/src/SkeletonData.cs @@ -42,12 +42,14 @@ namespace Spine { public List Skins { get; private set; } /** May be null. */ public Skin DefaultSkin; + public List Events { get; private set; } public List Animations { get; private set; } public SkeletonData () { Bones = new List(); Slots = new List(); Skins = new List(); + Events = new List(); Animations = new List(); } @@ -117,6 +119,21 @@ namespace Spine { return null; } + // --- Events. + + public void AddEvent (EventData eventData) { + if (eventData == null) throw new ArgumentNullException("eventData cannot be null."); + Events.Add(eventData); + } + + /** @return May be null. */ + public EventData findEvent (String eventDataName) { + if (eventDataName == null) throw new ArgumentNullException("eventDataName cannot be null."); + foreach (EventData eventData in Events) + if (eventData.Name == eventDataName) return eventData; + return null; + } + // --- Animations. public void AddAnimation (Animation animation) { diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index a6524a427..a51da200a 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -118,8 +118,7 @@ namespace Spine { // Slots. if (root.ContainsKey("slots")) { - var slots = (List)root["slots"]; - foreach (Dictionary slotMap in slots) { + foreach (Dictionary slotMap in (List)root["slots"]) { String slotName = (String)slotMap["name"]; String boneName = (String)slotMap["bone"]; BoneData boneData = skeletonData.FindBone(boneName); @@ -147,8 +146,7 @@ namespace Spine { // Skins. if (root.ContainsKey("skins")) { - var skinMap = (Dictionary)root["skins"]; - foreach (KeyValuePair entry in skinMap) { + foreach (KeyValuePair entry in (Dictionary)root["skins"]) { Skin skin = new Skin(entry.Key); foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); @@ -163,11 +161,21 @@ namespace Spine { } } + // Events. + if (root.ContainsKey("events")) { + foreach (KeyValuePair entry in (Dictionary)root["events"]) { + var entryMap = (Dictionary)entry.Value; + EventData eventData = new EventData(entry.Key); + eventData.Int = GetInt(entryMap, "int", 0); + eventData.Float = GetFloat(entryMap, "float", 0); + eventData.String = GetString(entryMap, "string", null); + skeletonData.AddEvent(eventData); + } + } // Animations. if (root.ContainsKey("animations")) { - var animationMap = (Dictionary)root["animations"]; - foreach (KeyValuePair entry in animationMap) + foreach (KeyValuePair entry in (Dictionary)root["animations"]) ReadAnimation(entry.Key, (Dictionary)entry.Value, skeletonData); } @@ -213,16 +221,28 @@ namespace Spine { private float GetFloat (Dictionary map, String name, float defaultValue) { if (!map.ContainsKey(name)) - return (float)defaultValue; + return defaultValue; return (float)map[name]; } + private int GetInt (Dictionary map, String name, int defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (int)map[name]; + } + private bool GetBoolean (Dictionary map, String name, bool defaultValue) { if (!map.ContainsKey(name)) - return (bool)defaultValue; + return defaultValue; return (bool)map[name]; } + private String GetString (Dictionary map, String name, String defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (String)map[name]; + } + public static float ToColor (String hexString, int colorIndex) { if (hexString.Length != 8) throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString); @@ -234,8 +254,7 @@ namespace Spine { float duration = 0; if (map.ContainsKey("bones")) { - var bonesMap = (Dictionary)map["bones"]; - foreach (KeyValuePair entry in bonesMap) { + foreach (KeyValuePair entry in (Dictionary)map["bones"]) { String boneName = entry.Key; int boneIndex = skeletonData.FindBoneIndex(boneName); if (boneIndex == -1) @@ -289,8 +308,7 @@ namespace Spine { } if (map.ContainsKey("slots")) { - var slotsMap = (Dictionary)map["slots"]; - foreach (KeyValuePair entry in slotsMap) { + foreach (KeyValuePair entry in (Dictionary)map["slots"]) { String slotName = entry.Key; int slotIndex = skeletonData.FindSlotIndex(slotName); var timelineMap = (Dictionary)entry.Value; @@ -331,6 +349,23 @@ namespace Spine { } } + if (map.ContainsKey("events")) { + var eventsMap = (List)map["events"]; + EventTimeline timeline = new EventTimeline(eventsMap.Count); + int frameIndex = 0; + foreach (Dictionary eventMap in eventsMap) { + EventData eventData = skeletonData.findEvent((String)eventMap["name"]); + if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]); + Event e = new Event(eventData); + e.Int = GetInt(eventMap, "int", eventData.Int); + e.Float = GetFloat(eventMap, "float", eventData.Float); + e.String = GetString(eventMap, "string", eventData.String); + timeline.setFrame(frameIndex++, (float)eventMap["time"], e); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.Frames[timeline.FrameCount - 1]); + } + if (map.ContainsKey("draworder")) { var values = (List)map["draworder"]; DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count); diff --git a/spine-xna/example/src/ExampleGame.cs b/spine-xna/example/src/ExampleGame.cs index 774be6dcc..5faadccb9 100644 --- a/spine-xna/example/src/ExampleGame.cs +++ b/spine-xna/example/src/ExampleGame.cs @@ -86,6 +86,11 @@ namespace Spine { } state = new AnimationState(stateData); + state.Start += new EventHandler(Start); + state.End += new EventHandler(End); + state.Complete += new EventHandler(Complete); + state.Event += new EventHandler(Event); + if (true) { state.SetAnimation("drawOrder", true); } else { @@ -140,5 +145,21 @@ namespace Spine { base.Draw(gameTime); } + + public void Start (object sender, EventArgs e) { + Console.WriteLine("start"); + } + + public void End (object sender, EventArgs e) { + Console.WriteLine("end"); + } + + public void Complete (object sender, CompleteArgs e) { + Console.WriteLine("complete " + e.LoopCount); + } + + public void Event (object sender, EventTriggeredArgs e) { + Console.WriteLine("event " + e.Event); + } } }