mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-22 02:06:03 +08:00
Event timeline for spine-csharp.
This commit is contained in:
parent
a16bb5b5d6
commit
d2d6919afb
@ -105,6 +105,8 @@
|
|||||||
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
||||||
<Compile Include="src\Bone.cs" />
|
<Compile Include="src\Bone.cs" />
|
||||||
<Compile Include="src\BoneData.cs" />
|
<Compile Include="src\BoneData.cs" />
|
||||||
|
<Compile Include="src\Event.cs" />
|
||||||
|
<Compile Include="src\EventData.cs" />
|
||||||
<Compile Include="src\Json.cs" />
|
<Compile Include="src\Json.cs" />
|
||||||
<Compile Include="src\Skeleton.cs" />
|
<Compile Include="src\Skeleton.cs" />
|
||||||
<Compile Include="src\SkeletonBounds.cs" />
|
<Compile Include="src\SkeletonBounds.cs" />
|
||||||
|
|||||||
@ -48,27 +48,47 @@ namespace Spine {
|
|||||||
Duration = duration;
|
Duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Poses the skeleton at the specified time for this animation. */
|
/** @deprecated */
|
||||||
public void Apply (Skeleton skeleton, float time, bool loop) {
|
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<Event> events) {
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
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<Timeline> timelines = Timelines;
|
List<Timeline> timelines = Timelines;
|
||||||
for (int i = 0, n = timelines.Count; i < n; i++)
|
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.
|
/** 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. */
|
* @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<Event> events, float alpha) {
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
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<Timeline> timelines = Timelines;
|
List<Timeline> timelines = Timelines;
|
||||||
for (int i = 0, n = timelines.Count; i < n; i++)
|
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. */
|
/** @param target After the first and before the last entry. */
|
||||||
@ -96,7 +116,7 @@ namespace Spine {
|
|||||||
|
|
||||||
public interface Timeline {
|
public interface Timeline {
|
||||||
/** Sets the value(s) for the specified time. */
|
/** 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<Event> firedEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Base class for frames that use an interpolation bezier curve. */
|
/** Base class for frames that use an interpolation bezier curve. */
|
||||||
@ -116,7 +136,7 @@ namespace Spine {
|
|||||||
curves = new float[(frameCount - 1) * 6];
|
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<Event> firedEvents);
|
||||||
|
|
||||||
public void SetLinear (int frameIndex) {
|
public void SetLinear (int frameIndex) {
|
||||||
curves[frameIndex * 6] = LINEAR;
|
curves[frameIndex * 6] = LINEAR;
|
||||||
@ -202,7 +222,7 @@ namespace Spine {
|
|||||||
Frames[frameIndex + 1] = angle;
|
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<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
@ -262,7 +282,7 @@ namespace Spine {
|
|||||||
Frames[frameIndex + 2] = y;
|
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<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
@ -292,7 +312,7 @@ namespace Spine {
|
|||||||
: base(frameCount) {
|
: 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<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
@ -341,7 +361,7 @@ namespace Spine {
|
|||||||
Frames[frameIndex + 4] = a;
|
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<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
@ -405,7 +425,7 @@ namespace Spine {
|
|||||||
AttachmentNames[frameIndex] = attachmentName;
|
AttachmentNames[frameIndex] = attachmentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply (Skeleton skeleton, float time, float alpha) {
|
public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
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<Event> 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 class DrawOrderTimeline : Timeline {
|
||||||
public float[] Frames { get; private set; } // time, ...
|
public float[] Frames { get; private set; } // time, ...
|
||||||
public int[][] DrawOrders { get; private set; }
|
public int[][] DrawOrders { get; private set; }
|
||||||
@ -441,7 +512,7 @@ namespace Spine {
|
|||||||
DrawOrders[frameIndex] = drawOrder;
|
DrawOrders[frameIndex] = drawOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply (Skeleton skeleton, float time, float alpha) {
|
public void Apply (Skeleton skeleton, float lastTime, float time, float alpha, List<Event> firedEvents) {
|
||||||
float[] frames = Frames;
|
float[] frames = Frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
|
|||||||
@ -38,28 +38,55 @@ namespace Spine {
|
|||||||
public class AnimationState {
|
public class AnimationState {
|
||||||
public AnimationStateData Data { get; private set; }
|
public AnimationStateData Data { get; private set; }
|
||||||
public Animation Animation { 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; }
|
public bool Loop { get; set; }
|
||||||
private Animation previous;
|
private Animation previous;
|
||||||
private float previousTime;
|
private float previousTime;
|
||||||
private bool previousLoop;
|
private bool previousLoop;
|
||||||
|
private QueueEntry currentQueueEntry;
|
||||||
private float mixTime, mixDuration;
|
private float mixTime, mixDuration;
|
||||||
|
private List<Event> events = new List<Event>();
|
||||||
private List<QueueEntry> queue = new List<QueueEntry>();
|
private List<QueueEntry> queue = new List<QueueEntry>();
|
||||||
|
|
||||||
|
public event EventHandler Start;
|
||||||
|
public event EventHandler End;
|
||||||
|
public event EventHandler<EventTriggeredArgs> Event;
|
||||||
|
public event EventHandler<CompleteArgs> Complete;
|
||||||
|
|
||||||
public AnimationState (AnimationStateData data) {
|
public AnimationState (AnimationStateData data) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update (float delta) {
|
public void Update (float delta) {
|
||||||
Time += delta;
|
time += delta;
|
||||||
previousTime += delta;
|
previousTime += delta;
|
||||||
mixTime += 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) {
|
if (queue.Count > 0) {
|
||||||
QueueEntry entry = queue[0];
|
QueueEntry entry = queue[0];
|
||||||
if (Time >= entry.delay) {
|
if (time >= entry.delay) {
|
||||||
SetAnimationInternal(entry.animation, entry.loop);
|
SetAnimationInternal(entry.animation, entry.loop, entry);
|
||||||
queue.RemoveAt(0);
|
queue.RemoveAt(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,35 +94,94 @@ namespace Spine {
|
|||||||
|
|
||||||
public void Apply (Skeleton skeleton) {
|
public void Apply (Skeleton skeleton) {
|
||||||
if (Animation == null) return;
|
if (Animation == null) return;
|
||||||
|
|
||||||
|
List<Event> events = this.events;
|
||||||
|
events.Clear();
|
||||||
|
|
||||||
if (previous != null) {
|
if (previous != null) {
|
||||||
previous.Apply(skeleton, previousTime, previousLoop);
|
previous.Apply(skeleton, int.MaxValue, previousTime, previousLoop, null);
|
||||||
float alpha = mixTime / mixDuration;
|
float alpha = mixTime / mixDuration;
|
||||||
if (alpha >= 1) {
|
if (alpha >= 1) {
|
||||||
alpha = 1;
|
alpha = 1;
|
||||||
previous = null;
|
previous = null;
|
||||||
}
|
}
|
||||||
Animation.Mix(skeleton, Time, Loop, alpha);
|
Animation.Mix(skeleton, currentLastTime, time, Loop, events, alpha);
|
||||||
} else
|
} 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAnimation (String animationName, bool loop) {
|
currentLastTime = time;
|
||||||
AddAnimation(animationName, loop, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAnimation (String animationName, bool loop, float delay) {
|
public void ClearAnimation () {
|
||||||
|
previous = null;
|
||||||
|
Animation = null;
|
||||||
|
queue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
Animation animation = Data.SkeletonData.FindAnimation(animationName);
|
||||||
if (animation == null) throw new ArgumentException("Animation not found: " + 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) {
|
/** Set the current animation. Any queued animations are cleared and the current animation time is set to 0.
|
||||||
AddAnimation(animation, loop, 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.
|
/** 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. */
|
* @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();
|
QueueEntry entry = new QueueEntry();
|
||||||
entry.animation = animation;
|
entry.animation = animation;
|
||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
@ -110,54 +196,59 @@ namespace Spine {
|
|||||||
entry.delay = delay;
|
entry.delay = delay;
|
||||||
|
|
||||||
queue.Add(entry);
|
queue.Add(entry);
|
||||||
}
|
return 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */
|
/** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */
|
||||||
public bool IsComplete () {
|
public bool IsComplete () {
|
||||||
return Animation == null || Time >= Animation.Duration;
|
return Animation == null || time >= Animation.Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public String ToString () {
|
override public String ToString () {
|
||||||
return (Animation != null && Animation.Name != null) ? Animation.Name : base.ToString();
|
return (Animation != null && Animation.Name != null) ? Animation.Name : base.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EventTriggeredArgs : EventArgs {
|
||||||
|
public Event Event { get; private set; }
|
||||||
|
|
||||||
|
public EventTriggeredArgs (Event e) {
|
||||||
|
Event = e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueueEntry {
|
public class CompleteArgs : EventArgs {
|
||||||
public Spine.Animation animation;
|
public int LoopCount { get; private set; }
|
||||||
public bool loop;
|
|
||||||
public float delay;
|
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<EventTriggeredArgs> Event;
|
||||||
|
public event EventHandler<CompleteArgs> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
spine-csharp/src/Event.cs
Normal file
51
spine-csharp/src/Event.cs
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
spine-csharp/src/EventData.cs
Normal file
52
spine-csharp/src/EventData.cs
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -133,8 +133,10 @@ namespace Spine {
|
|||||||
} else
|
} else
|
||||||
polygon = new Polygon();
|
polygon = new Polygon();
|
||||||
polygons.Add(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);
|
boundingBox.ComputeWorldVertices(x, y, slot.Bone, polygon.Vertices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,12 +42,14 @@ namespace Spine {
|
|||||||
public List<Skin> Skins { get; private set; }
|
public List<Skin> Skins { get; private set; }
|
||||||
/** May be null. */
|
/** May be null. */
|
||||||
public Skin DefaultSkin;
|
public Skin DefaultSkin;
|
||||||
|
public List<EventData> Events { get; private set; }
|
||||||
public List<Animation> Animations { get; private set; }
|
public List<Animation> Animations { get; private set; }
|
||||||
|
|
||||||
public SkeletonData () {
|
public SkeletonData () {
|
||||||
Bones = new List<BoneData>();
|
Bones = new List<BoneData>();
|
||||||
Slots = new List<SlotData>();
|
Slots = new List<SlotData>();
|
||||||
Skins = new List<Skin>();
|
Skins = new List<Skin>();
|
||||||
|
Events = new List<EventData>();
|
||||||
Animations = new List<Animation>();
|
Animations = new List<Animation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +119,21 @@ namespace Spine {
|
|||||||
return null;
|
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.
|
// --- Animations.
|
||||||
|
|
||||||
public void AddAnimation (Animation animation) {
|
public void AddAnimation (Animation animation) {
|
||||||
|
|||||||
@ -118,8 +118,7 @@ namespace Spine {
|
|||||||
|
|
||||||
// Slots.
|
// Slots.
|
||||||
if (root.ContainsKey("slots")) {
|
if (root.ContainsKey("slots")) {
|
||||||
var slots = (List<Object>)root["slots"];
|
foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
|
||||||
foreach (Dictionary<String, Object> slotMap in slots) {
|
|
||||||
String slotName = (String)slotMap["name"];
|
String slotName = (String)slotMap["name"];
|
||||||
String boneName = (String)slotMap["bone"];
|
String boneName = (String)slotMap["bone"];
|
||||||
BoneData boneData = skeletonData.FindBone(boneName);
|
BoneData boneData = skeletonData.FindBone(boneName);
|
||||||
@ -147,8 +146,7 @@ namespace Spine {
|
|||||||
|
|
||||||
// Skins.
|
// Skins.
|
||||||
if (root.ContainsKey("skins")) {
|
if (root.ContainsKey("skins")) {
|
||||||
var skinMap = (Dictionary<String, Object>)root["skins"];
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) {
|
||||||
foreach (KeyValuePair<String, Object> entry in skinMap) {
|
|
||||||
Skin skin = new Skin(entry.Key);
|
Skin skin = new Skin(entry.Key);
|
||||||
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
|
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
||||||
@ -163,11 +161,21 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Events.
|
||||||
|
if (root.ContainsKey("events")) {
|
||||||
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
|
||||||
|
var entryMap = (Dictionary<String, Object>)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.
|
// Animations.
|
||||||
if (root.ContainsKey("animations")) {
|
if (root.ContainsKey("animations")) {
|
||||||
var animationMap = (Dictionary<String, Object>)root["animations"];
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"])
|
||||||
foreach (KeyValuePair<String, Object> entry in animationMap)
|
|
||||||
ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
|
ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,16 +221,28 @@ namespace Spine {
|
|||||||
|
|
||||||
private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
|
private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
|
||||||
if (!map.ContainsKey(name))
|
if (!map.ContainsKey(name))
|
||||||
return (float)defaultValue;
|
return defaultValue;
|
||||||
return (float)map[name];
|
return (float)map[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetInt (Dictionary<String, Object> map, String name, int defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (int)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
|
private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
|
||||||
if (!map.ContainsKey(name))
|
if (!map.ContainsKey(name))
|
||||||
return (bool)defaultValue;
|
return defaultValue;
|
||||||
return (bool)map[name];
|
return (bool)map[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String GetString (Dictionary<String, Object> map, String name, String defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (String)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
public static float ToColor (String hexString, int colorIndex) {
|
public static float ToColor (String hexString, int colorIndex) {
|
||||||
if (hexString.Length != 8)
|
if (hexString.Length != 8)
|
||||||
throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
|
throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
|
||||||
@ -234,8 +254,7 @@ namespace Spine {
|
|||||||
float duration = 0;
|
float duration = 0;
|
||||||
|
|
||||||
if (map.ContainsKey("bones")) {
|
if (map.ContainsKey("bones")) {
|
||||||
var bonesMap = (Dictionary<String, Object>)map["bones"];
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) {
|
||||||
foreach (KeyValuePair<String, Object> entry in bonesMap) {
|
|
||||||
String boneName = entry.Key;
|
String boneName = entry.Key;
|
||||||
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
||||||
if (boneIndex == -1)
|
if (boneIndex == -1)
|
||||||
@ -289,8 +308,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (map.ContainsKey("slots")) {
|
if (map.ContainsKey("slots")) {
|
||||||
var slotsMap = (Dictionary<String, Object>)map["slots"];
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) {
|
||||||
foreach (KeyValuePair<String, Object> entry in slotsMap) {
|
|
||||||
String slotName = entry.Key;
|
String slotName = entry.Key;
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||||
var timelineMap = (Dictionary<String, Object>)entry.Value;
|
var timelineMap = (Dictionary<String, Object>)entry.Value;
|
||||||
@ -331,6 +349,23 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (map.ContainsKey("events")) {
|
||||||
|
var eventsMap = (List<Object>)map["events"];
|
||||||
|
EventTimeline timeline = new EventTimeline(eventsMap.Count);
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<String, Object> 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")) {
|
if (map.ContainsKey("draworder")) {
|
||||||
var values = (List<Object>)map["draworder"];
|
var values = (List<Object>)map["draworder"];
|
||||||
DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count);
|
DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count);
|
||||||
|
|||||||
@ -86,6 +86,11 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = new AnimationState(stateData);
|
state = new AnimationState(stateData);
|
||||||
|
state.Start += new EventHandler(Start);
|
||||||
|
state.End += new EventHandler(End);
|
||||||
|
state.Complete += new EventHandler<CompleteArgs>(Complete);
|
||||||
|
state.Event += new EventHandler<EventTriggeredArgs>(Event);
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
state.SetAnimation("drawOrder", true);
|
state.SetAnimation("drawOrder", true);
|
||||||
} else {
|
} else {
|
||||||
@ -140,5 +145,21 @@ namespace Spine {
|
|||||||
|
|
||||||
base.Draw(gameTime);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user