Merge branch 'master' into 3.6-beta

This commit is contained in:
NathanSweet 2016-12-06 16:57:05 +01:00
commit 6fb9a4e991
37 changed files with 348 additions and 130 deletions

View File

@ -366,7 +366,7 @@ public class AnimationState {
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if (from.mixingFrom != null) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
queue.start(current); queue.start(current);
@ -485,7 +485,7 @@ public class AnimationState {
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? int.MAX_VALUE : entry.animationEnd; entry.trackEnd = int.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;

View File

@ -126,7 +126,7 @@ public class Atlas {
region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])); region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits
region.pads = Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])); region.pads = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
reader.readTuple(tuple); reader.readTuple(tuple);
} }

View File

@ -582,7 +582,7 @@ void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEnt
from->timelinesRotationCount = 0; from->timelinesRotationCount = 0;
/* If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. */ /* If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. */
if (from->mixingFrom) current->mixAlpha *= MIN(from->mixTime / from->mixDuration, 1); if (from->mixingFrom && from->mixDuration > 0) current->mixAlpha *= MIN(from->mixTime / from->mixDuration, 1);
} }
_spEventQueue_start(internal->queue, current); _spEventQueue_start(internal->queue, current);
@ -715,7 +715,7 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd
entry->trackTime = 0; entry->trackTime = 0;
entry->trackLast = -1; entry->trackLast = -1;
entry->nextTrackLast = -1; entry->nextTrackLast = -1;
entry->trackEnd = loop ? (float)INT_MAX : entry->animationEnd; entry->trackEnd = (float)INT_MAX;
entry->timeScale = 1; entry->timeScale = 1;
entry->alpha = 1; entry->alpha = 1;

View File

@ -66,11 +66,11 @@ typedef struct {
} Str; } Str;
static void trim (Str* str) { static void trim (Str* str) {
while (isspace(*str->begin) && str->begin < str->end) while (isspace((unsigned char)*str->begin) && str->begin < str->end)
(str->begin)++; (str->begin)++;
if (str->begin == str->end) return; if (str->begin == str->end) return;
str->end--; str->end--;
while (isspace(*str->end) && str->end >= str->begin) while (isspace((unsigned char)*str->end) && str->end >= str->begin)
str->end--; str->end--;
str->end++; str->end++;
} }

View File

@ -373,7 +373,7 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) { for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input); float time = readFloat(input);
float mix = readFloat(input); float mix = readFloat(input);
char bendDirection = readSByte(input); signed char bendDirection = readSByte(input);
spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection); spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection);
if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex); if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
} }

View File

@ -30,11 +30,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Spine { namespace Spine {
public class AnimationState { public class AnimationState {
private static Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0); static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
private AnimationStateData data; private AnimationStateData data;
private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>(); private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
@ -44,6 +43,8 @@ namespace Spine {
private bool animationsChanged; private bool animationsChanged;
private float timeScale = 1; private float timeScale = 1;
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
public AnimationStateData Data { get { return data; } } public AnimationStateData Data { get { return data; } }
/// <summary>A list of tracks that have animations, which may contain nulls.</summary> /// <summary>A list of tracks that have animations, which may contain nulls.</summary>
public ExposedList<TrackEntry> Tracks { get { return tracks; } } public ExposedList<TrackEntry> Tracks { get { return tracks; } }
@ -58,7 +59,7 @@ namespace Spine {
public AnimationState (AnimationStateData data) { public AnimationState (AnimationStateData data) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null."); if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.data = data; this.data = data;
this.queue = new EventQueue(this, HandleAnimationsChanged); this.queue = new EventQueue(this, HandleAnimationsChanged, trackEntryPool);
} }
void HandleAnimationsChanged () { void HandleAnimationsChanged () {
@ -215,7 +216,7 @@ namespace Spine {
float alpha = from.alpha * entry.mixAlpha * (1 - mix); float alpha = from.alpha * entry.mixAlpha * (1 - mix);
bool firstFrame = entry.timelinesRotation.Count == 0; bool firstFrame = entry.timelinesRotation.Count == 0;
if (firstFrame) entry.timelinesRotation.Capacity = timelineCount << 1; if (firstFrame) entry.timelinesRotation.EnsureCapacity(timelines.Count << 1);
var timelinesRotation = entry.timelinesRotation.Items; var timelinesRotation = entry.timelinesRotation.Items;
for (int i = 0; i < timelineCount; i++) { for (int i = 0; i < timelineCount; i++) {
@ -389,7 +390,7 @@ namespace Spine {
from.timelinesRotation.Clear(); from.timelinesRotation.Clear();
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if (from.mixingFrom != null) from.mixAlpha *= Math.Min(from.mixTime / from.mixDuration, 1); if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.Min(from.mixTime / from.mixDuration, 1);
} }
queue.Start(current); queue.Start(current);
@ -526,32 +527,32 @@ namespace Spine {
/// <param name="last">May be null.</param> /// <param name="last">May be null.</param>
private TrackEntry NewTrackEntry (int trackIndex, Animation animation, bool loop, TrackEntry last) { private TrackEntry NewTrackEntry (int trackIndex, Animation animation, bool loop, TrackEntry last) {
return new TrackEntry { TrackEntry entry = trackEntryPool.Obtain(); // Pooling
trackIndex = trackIndex, entry.trackIndex = trackIndex;
animation = animation, entry.animation = animation;
loop = loop, entry.loop = loop;
eventThreshold = 0, entry.eventThreshold = 0;
attachmentThreshold = 0, entry.attachmentThreshold = 0;
drawOrderThreshold = 0, entry.drawOrderThreshold = 0;
animationStart = 0, entry.animationStart = 0;
animationEnd = animation.duration, entry.animationEnd = animation.Duration;
animationLast = -1, entry.animationLast = -1;
nextAnimationLast = -1, entry.nextAnimationLast = -1;
delay = 0, entry.delay = 0;
trackTime = 0, entry.trackTime = 0;
trackLast = -1, entry.trackLast = -1;
nextTrackLast = -1, entry.nextTrackLast = -1;
trackEnd = loop ? int.MaxValue : animation.duration, entry.trackEnd = float.MaxValue; // loop ? float.MaxValue : animation.Duration;
timeScale = 1, entry.timeScale = 1;
alpha = 1, entry.alpha = 1;
mixAlpha = 1, entry.mixAlpha = 1;
mixTime = 0, entry.mixTime = 0;
mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation), entry.mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation);
}; return entry;
} }
private void DisposeNext (TrackEntry entry) { private void DisposeNext (TrackEntry entry) {
@ -627,7 +628,7 @@ namespace Spine {
} }
override public String ToString () { override public String ToString () {
var buffer = new StringBuilder(); var buffer = new System.Text.StringBuilder();
for (int i = 0, n = tracks.Count; i < n; i++) { for (int i = 0, n = tracks.Count; i < n; i++) {
TrackEntry entry = tracks.Items[i]; TrackEntry entry = tracks.Items[i];
if (entry == null) continue; if (entry == null) continue;
@ -646,7 +647,7 @@ namespace Spine {
} }
/// <summary>State for the playback of an animation.</summary> /// <summary>State for the playback of an animation.</summary>
public class TrackEntry { public class TrackEntry : Pool<TrackEntry>.IPoolable {
internal Animation animation; internal Animation animation;
internal TrackEntry next, mixingFrom; internal TrackEntry next, mixingFrom;
@ -657,9 +658,25 @@ namespace Spine {
internal float animationStart, animationEnd, animationLast, nextAnimationLast; internal float animationStart, animationEnd, animationLast, nextAnimationLast;
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f; internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
internal float alpha, mixTime, mixDuration, mixAlpha; internal float alpha, mixTime, mixDuration, mixAlpha;
internal readonly ExposedList<bool> timelinesFirst = new ExposedList<bool>(), timelinesLast = new ExposedList<bool>(); internal readonly ExposedList<bool> timelinesFirst = new ExposedList<bool>();
internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>(); internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
// IPoolable.Reset()
public void Reset () {
next = null;
mixingFrom = null;
animation = null;
timelinesFirst.Clear();
timelinesRotation.Clear();
Start = null;
Interrupt = null;
End = null;
Dispose = null;
Complete = null;
Event = null;
}
/// <summary>The index of the track where this entry is either current or queued.</summary> /// <summary>The index of the track where this entry is either current or queued.</summary>
public int TrackIndex { get { return trackIndex; } } public int TrackIndex { get { return trackIndex; } }
@ -824,11 +841,13 @@ namespace Spine {
public bool drainDisabled; public bool drainDisabled;
private readonly AnimationState state; private readonly AnimationState state;
private readonly Pool<TrackEntry> trackEntryPool;
public event Action AnimationsChanged; public event Action AnimationsChanged;
public EventQueue (AnimationState state, Action HandleAnimationsChanged) { public EventQueue (AnimationState state, Action HandleAnimationsChanged, Pool<TrackEntry> trackEntryPool) {
this.state = state; this.state = state;
this.AnimationsChanged += HandleAnimationsChanged; this.AnimationsChanged += HandleAnimationsChanged;
this.trackEntryPool = trackEntryPool;
} }
struct EventQueueEntry { struct EventQueueEntry {
@ -901,6 +920,7 @@ namespace Spine {
case EventType.Dispose: case EventType.Dispose:
trackEntry.OnDispose(); trackEntry.OnDispose();
state.OnDispose(trackEntry); state.OnDispose(trackEntry);
trackEntryPool.Free(trackEntry); // Pooling
break; break;
case EventType.Complete: case EventType.Complete:
trackEntry.OnComplete(); trackEntry.OnComplete();
@ -921,4 +941,57 @@ namespace Spine {
eventQueueEntries.Clear(); eventQueueEntries.Clear();
} }
} }
public class Pool<T> where T : class, new() {
public readonly int max;
readonly Stack<T> freeObjects;
public int Count { get { return freeObjects.Count; } }
public int Peak { get; private set; }
public Pool (int initialCapacity = 16, int max = int.MaxValue) {
freeObjects = new Stack<T>(initialCapacity);
this.max = max;
}
public T Obtain () {
return freeObjects.Count == 0 ? new T() : freeObjects.Pop();
}
public void Free (T obj) {
if (obj == null) throw new ArgumentNullException("obj", "obj cannot be null");
if (freeObjects.Count < max) {
freeObjects.Push(obj);
Peak = Math.Max(Peak, freeObjects.Count);
}
Reset(obj);
}
// protected void FreeAll (List<T> objects) {
// if (objects == null) throw new ArgumentNullException("objects", "objects cannot be null.");
// var freeObjects = this.freeObjects;
// int max = this.max;
// for (int i = 0; i < objects.Count; i++) {
// T obj = objects[i];
// if (obj == null) continue;
// if (freeObjects.Count < max) freeObjects.Push(obj);
// Reset(obj);
// }
// Peak = Math.Max(Peak, freeObjects.Count);
// }
public void Clear () {
freeObjects.Clear();
}
protected void Reset (T obj) {
var poolable = obj as IPoolable;
if (poolable != null) poolable.Reset();
}
public interface IPoolable {
void Reset ();
}
}
} }

View File

@ -134,7 +134,7 @@ public class AnimationStateTests {
expect(0, "end", 1, 1.1f), // expect(0, "end", 1, 1.1f), //
expect(0, "dispose", 1, 1.1f) // expect(0, "dispose", 1, 1.1f) //
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false).setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("1/60 time step, dispose queued", // 2 setup("1/60 time step, dispose queued", // 2
@ -160,7 +160,7 @@ public class AnimationStateTests {
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0);
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false).setTrackEnd(1);
run(1 / 60f, 1000, null); run(1 / 60f, 1000, null);
setup("30 time step", // 3 setup("30 time step", // 3
@ -172,7 +172,7 @@ public class AnimationStateTests {
expect(0, "end", 30, 60), // expect(0, "end", 30, 60), //
expect(0, "dispose", 30, 60) // expect(0, "dispose", 30, 60) //
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false).setTrackEnd(1);
run(30, 1000, null); run(30, 1000, null);
setup("1 time step", // 4 setup("1 time step", // 4
@ -184,7 +184,7 @@ public class AnimationStateTests {
expect(0, "end", 1, 2), // expect(0, "end", 1, 2), //
expect(0, "dispose", 1, 2) // expect(0, "dispose", 1, 2) //
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false).setTrackEnd(1);
run(1, 1.01f, null); run(1, 1.01f, null);
setup("interrupt", // 5 setup("interrupt", // 5
@ -220,7 +220,7 @@ public class AnimationStateTests {
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false);
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0).setTrackEnd(1);
run(0.1f, 4f, null); run(0.1f, 4f, null);
setup("interrupt with delay", // 6 setup("interrupt with delay", // 6
@ -242,7 +242,7 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 1.6f) // expect(1, "dispose", 1, 1.6f) //
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false);
state.addAnimation(0, "events1", false, 0.5f); state.addAnimation(0, "events1", false, 0.5f).setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("interrupt with delay and mix time", // 7 setup("interrupt with delay and mix time", // 7
@ -268,7 +268,7 @@ public class AnimationStateTests {
); );
stateData.setMix("events0", "events1", 0.7f); stateData.setMix("events0", "events1", 0.7f);
state.setAnimation(0, "events0", true); state.setAnimation(0, "events0", true);
state.addAnimation(0, "events1", false, 0.9f); state.addAnimation(0, "events1", false, 0.9f).setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("animation 0 events do not fire during mix", // 8 setup("animation 0 events do not fire during mix", // 8
@ -291,7 +291,7 @@ public class AnimationStateTests {
); );
stateData.setDefaultMix(0.7f); stateData.setDefaultMix(0.7f);
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false);
state.addAnimation(0, "events1", false, 0.4f); state.addAnimation(0, "events1", false, 0.4f).setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("event threshold, some animation 0 events fire during mix", // 9 setup("event threshold, some animation 0 events fire during mix", // 9
@ -317,7 +317,7 @@ public class AnimationStateTests {
); );
stateData.setMix("events0", "events1", 0.7f); stateData.setMix("events0", "events1", 0.7f);
state.setAnimation(0, "events0", false).setEventThreshold(0.5f); state.setAnimation(0, "events0", false).setEventThreshold(0.5f);
state.addAnimation(0, "events1", false, 0.4f); state.addAnimation(0, "events1", false, 0.4f).setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("event threshold, all animation 0 events fire during mix", // 10 setup("event threshold, all animation 0 events fire during mix", // 10
@ -344,7 +344,9 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 1.9f) // expect(1, "dispose", 1, 1.9f) //
); );
state.setAnimation(0, "events0", true).setEventThreshold(1); state.setAnimation(0, "events0", true).setEventThreshold(1);
state.addAnimation(0, "events1", false, 0.8f).setMixDuration(0.7f); entry = state.addAnimation(0, "events1", false, 0.8f);
entry.setMixDuration(0.7f);
entry.setTrackEnd(1);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("looping", // 11 setup("looping", // 11
@ -393,7 +395,7 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 3.1f) // expect(1, "dispose", 1, 3.1f) //
); );
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false);
state.addAnimation(0, "events1", false, 2); state.addAnimation(0, "events1", false, 2).setTrackEnd(1);
run(0.1f, 4f, null); run(0.1f, 4f, null);
setup("interrupt animation after first loop complete", // 13 setup("interrupt animation after first loop complete", // 13
@ -424,7 +426,7 @@ public class AnimationStateTests {
state.setAnimation(0, "events0", true); state.setAnimation(0, "events0", true);
run(0.1f, 6, new TestListener() { run(0.1f, 6, new TestListener() {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 1.4f)) state.addAnimation(0, "events1", false, 0); if (MathUtils.isEqual(time, 1.4f)) state.addAnimation(0, "events1", false, 0).setTrackEnd(1);
} }
}); });
@ -437,7 +439,7 @@ public class AnimationStateTests {
expect(0, "end", 1, 1.1f), // expect(0, "end", 1, 1.1f), //
expect(0, "dispose", 1, 1.1f) // expect(0, "dispose", 1, 1.1f) //
); );
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0).setTrackEnd(1);
run(0.1f, 1.9f, null); run(0.1f, 1.9f, null);
setup("end time beyond non-looping animation duration", // 15 setup("end time beyond non-looping animation duration", // 15
@ -495,6 +497,7 @@ public class AnimationStateTests {
entry.setAnimationStart(0.2f); entry.setAnimationStart(0.2f);
entry.setAnimationLast(0.2f); entry.setAnimationLast(0.2f);
entry.setAnimationEnd(0.8f); entry.setAnimationEnd(0.8f);
entry.setTrackEnd(1);
run(0.1f, 1.8f, null); run(0.1f, 1.8f, null);
setup("mix out looping with animation start and end", // 19 setup("mix out looping with animation start and end", // 19
@ -524,7 +527,9 @@ public class AnimationStateTests {
entry.setAnimationLast(0.2f); entry.setAnimationLast(0.2f);
entry.setAnimationEnd(0.8f); entry.setAnimationEnd(0.8f);
entry.setEventThreshold(1); entry.setEventThreshold(1);
state.addAnimation(0, "events1", false, 0.7f).setMixDuration(0.7f); entry = state.addAnimation(0, "events1", false, 0.7f);
entry.setMixDuration(0.7f);
entry.setTrackEnd(1);
run(0.1f, 20, null); run(0.1f, 20, null);
setup("setAnimation with track entry mix", // 20 setup("setAnimation with track entry mix", // 20
@ -552,7 +557,11 @@ public class AnimationStateTests {
state.setAnimation(0, "events0", true); state.setAnimation(0, "events0", true);
run(0.1f, 1000, new TestListener() { run(0.1f, 1000, new TestListener() {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 1f)) state.setAnimation(0, "events1", false).setMixDuration(0.7f); if (MathUtils.isEqual(time, 1f)) {
TrackEntry entry = state.setAnimation(0, "events1", false);
entry.setMixDuration(0.7f);
entry.setTrackEnd(1);
}
} }
}); });
@ -593,7 +602,7 @@ public class AnimationStateTests {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 0.8f)) { if (MathUtils.isEqual(time, 0.8f)) {
state.setAnimation(0, "events0", false); // First should be ignored. state.setAnimation(0, "events0", false); // First should be ignored.
state.setAnimation(0, "events2", false); state.setAnimation(0, "events2", false).setTrackEnd(1);
} }
} }
}); });
@ -655,7 +664,7 @@ public class AnimationStateTests {
} }
if (MathUtils.isEqual(time, 0.4f)) { if (MathUtils.isEqual(time, 0.4f)) {
state.setAnimation(0, "events1", false); // First should be ignored. state.setAnimation(0, "events1", false); // First should be ignored.
state.setAnimation(0, "events0", false); state.setAnimation(0, "events0", false).setTrackEnd(1);
} }
} }
}); });
@ -669,7 +678,7 @@ public class AnimationStateTests {
expect(0, "end", 1, 6.1f), // expect(0, "end", 1, 6.1f), //
expect(0, "dispose", 1, 6.1f) // expect(0, "dispose", 1, 6.1f) //
); );
state.addAnimation(0, "events0", false, 5); state.addAnimation(0, "events0", false, 5).setTrackEnd(1);
run(0.1f, 10, null); run(0.1f, 10, null);
setup("setAnimation during AnimationStateListener"); // 24 setup("setAnimation during AnimationStateListener"); // 24
@ -700,7 +709,7 @@ public class AnimationStateTests {
}); });
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0);
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.setAnimation(1, "events1", false); state.setAnimation(1, "events1", false).setTrackEnd(1);
run(0.1f, 10, null); run(0.1f, 10, null);
setup("clearTrack", // 25 setup("clearTrack", // 25
@ -710,7 +719,7 @@ public class AnimationStateTests {
expect(0, "end", 0.7f, 0.7f), // expect(0, "end", 0.7f, 0.7f), //
expect(0, "dispose", 0.7f, 0.7f) // expect(0, "dispose", 0.7f, 0.7f) //
); );
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0).setTrackEnd(1);
run(0.1f, 10, new TestListener() { run(0.1f, 10, new TestListener() {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 0.7f)) state.clearTrack(0); if (MathUtils.isEqual(time, 0.7f)) state.clearTrack(0);
@ -732,7 +741,7 @@ public class AnimationStateTests {
expect(-1, "end", 0.2f, 1), // expect(-1, "end", 0.2f, 1), //
expect(-1, "dispose", 0.2f, 1) // expect(-1, "dispose", 0.2f, 1) //
); );
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0).setTrackEnd(1);
run(0.1f, 10, new TestListener() { run(0.1f, 10, new TestListener() {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 0.7f)) state.setEmptyAnimation(0, 0); if (MathUtils.isEqual(time, 0.7f)) state.setEmptyAnimation(0, 0);
@ -768,7 +777,7 @@ public class AnimationStateTests {
}); });
state.addAnimation(0, "events0", false, 0); state.addAnimation(0, "events0", false, 0);
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.setAnimation(1, "events1", false); state.setAnimation(1, "events1", false).setTrackEnd(1);
run(0.1f, 10, null); run(0.1f, 10, null);
if (counter.get() != 15082016) { if (counter.get() != 15082016) {
log("TEST 26 FAILED! " + counter); log("TEST 26 FAILED! " + counter);

View File

@ -185,6 +185,7 @@ public class AnimationState {
} }
} }
queueEvents(current, animationTime); queueEvents(current, animationTime);
events.clear();
current.nextAnimationLast = animationTime; current.nextAnimationLast = animationTime;
current.nextTrackLast = current.trackTime; current.nextTrackLast = current.trackTime;
} }
@ -230,7 +231,8 @@ public class AnimationState {
} }
} }
queueEvents(from, animationTime); if (entry.mixDuration > 0) queueEvents(from, animationTime);
this.events.clear();
from.nextAnimationLast = animationTime; from.nextAnimationLast = animationTime;
from.nextTrackLast = from.trackTime; from.nextTrackLast = from.trackTime;
@ -330,7 +332,6 @@ public class AnimationState {
if (event.time < animationStart) continue; // Discard events outside animation start/end. if (event.time < animationStart) continue; // Discard events outside animation start/end.
queue.event(entry, events.get(i)); queue.event(entry, events.get(i));
} }
events.clear();
} }
/** Removes all animations from all tracks, leaving skeletons in their previous pose. /** Removes all animations from all tracks, leaving skeletons in their previous pose.
@ -385,7 +386,7 @@ public class AnimationState {
from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in. from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if (from.mixingFrom != null) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
queue.start(current); queue.start(current);
@ -539,7 +540,7 @@ public class AnimationState {
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Integer.MAX_VALUE : entry.animationEnd; entry.trackEnd = Float.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
@ -752,10 +753,10 @@ public class AnimationState {
this.trackTime = trackTime; this.trackTime = trackTime;
} }
/** The track time in seconds when this animation will be removed from the track. Defaults to the animation /** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float
* {@link Animation#duration} for non-looping animations and the highest float possible for looping animations. If the track * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time
* end time is reached, no other animations are queued for playback, and mixing from any previous animations is complete, * is reached, no other animations are queued for playback, and mixing from any previous animations is complete, then the
* then the properties keyed by the animation are set to the setup pose and the track is cleared. * properties keyed by the animation are set to the setup pose and the track is cleared.
* <p> * <p>
* It may be desired to use {@link AnimationState#addEmptyAnimation(int, float, float)} to mix the properties back to the * It may be desired to use {@link AnimationState#addEmptyAnimation(int, float, float)} to mix the properties back to the
* setup pose over time, rather than have it happen instantly. */ * setup pose over time, rather than have it happen instantly. */

View File

@ -541,7 +541,7 @@ function AnimationState:setCurrent (index, current, interrupt)
from.timelinesRotation = {}; from.timelinesRotation = {};
-- If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. -- If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if from.mixingFrom then current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1) end if from.mixingFrom and from.mixDuration > 0 then current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1) end
end end
queue:start(current) queue:start(current)
@ -665,11 +665,7 @@ function AnimationState:trackEntry (trackIndex, animation, loop, last)
entry.trackTime = 0 entry.trackTime = 0
entry.trackLast = -1 entry.trackLast = -1
entry.nextTrackLast = -1 entry.nextTrackLast = -1
if loop then entry.trackEnd = 999999999
entry.trackEnd = 999999999
else
entry.trackEnd = entry.animationEnd
end
entry.timeScale = 1 entry.timeScale = 1
entry.alpha = 1 entry.alpha = 1

View File

@ -160,6 +160,7 @@ The configuration object has the following fields:
* `atlas`: required, path to the `.atlas` file, absolute or relative, e.g. "assets/animation.atlas" * `atlas`: required, path to the `.atlas` file, absolute or relative, e.g. "assets/animation.atlas"
* `animation`: required, the name of the animation to play back * `animation`: required, the name of the animation to play back
* `imagesPath`: optional, the location of images on the server to load atlas pages from. If omitted, atlas `.png` page files are loaded relative to the `.atlas` file. * `imagesPath`: optional, the location of images on the server to load atlas pages from. If omitted, atlas `.png` page files are loaded relative to the `.atlas` file.
* `atlasPages`: optional, the list of atlas page images, e.g. `atlasPages: ["assets/page1.png", "assets/page2.png"]` when using code, or `data-atlas-pages="assets/page1.png,assets/page2.png"` on case of HTML instantiation. Use this if you have a multi-page atlas. If ommited, only one atlas page image is loaded based on the atlas file name, replacing `.atlas` with `.png`.
* `skin`: optional, the name of the skin to use. Defaults to `default` if omitted. * `skin`: optional, the name of the skin to use. Defaults to `default` if omitted.
* `loop`: optional, whether to loop the animation or not. Defaults to `true` if omitted. * `loop`: optional, whether to loop the animation or not. Defaults to `true` if omitted.
* `scale`: optional, the scaling factor to apply when loading the `.json` file. Defaults to `1` if omitted. Irrelevant if `data-fit-to-canavs` is `true`. * `scale`: optional, the scaling factor to apply when loading the `.json` file. Defaults to `1` if omitted. Irrelevant if `data-fit-to-canavs` is `true`.

View File

@ -1543,6 +1543,7 @@ declare module spine {
atlas: string; atlas: string;
animation: string; animation: string;
imagesPath: string; imagesPath: string;
atlasPages: string[];
skin: string; skin: string;
loop: boolean; loop: boolean;
scale: number; scale: number;

View File

@ -1568,7 +1568,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1680,7 +1680,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;
@ -7899,7 +7899,14 @@ var spine;
var assets = this.assetManager = new spine.webgl.AssetManager(gl); var assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas); assets.loadText(config.atlas);
assets.loadText(config.json); assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png")); if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
}
else {
for (var i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(function () { _this.load(); }); requestAnimationFrame(function () { _this.load(); });
} }
SpineWidget.prototype.validateConfig = function (config) { SpineWidget.prototype.validateConfig = function (config) {
@ -8082,6 +8089,8 @@ var spine;
config.animation = widget.getAttribute("data-animation"); config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path")) if (widget.getAttribute("data-images-path"))
config.imagesPath = widget.getAttribute("data-images-path"); config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages"))
config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin")) if (widget.getAttribute("data-skin"))
config.skin = widget.getAttribute("data-skin"); config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop")) if (widget.getAttribute("data-loop"))

File diff suppressed because one or more lines are too long

View File

@ -1568,7 +1568,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1680,7 +1680,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -1221,7 +1221,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -1221,7 +1221,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -1221,7 +1221,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -1473,6 +1473,7 @@ declare module spine {
atlas: string; atlas: string;
animation: string; animation: string;
imagesPath: string; imagesPath: string;
atlasPages: string[];
skin: string; skin: string;
loop: boolean; loop: boolean;
scale: number; scale: number;

View File

@ -1221,7 +1221,7 @@ var spine;
current.mixingFrom = from; current.mixingFrom = from;
current.mixTime = 0; current.mixTime = 0;
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
if (from.mixingFrom != null) if (from.mixingFrom != null && from.mixDuration > 0)
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.mixAlpha = 1; entry.mixAlpha = 1;
@ -7478,7 +7478,14 @@ var spine;
var assets = this.assetManager = new spine.webgl.AssetManager(gl); var assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas); assets.loadText(config.atlas);
assets.loadText(config.json); assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png")); if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
}
else {
for (var i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(function () { _this.load(); }); requestAnimationFrame(function () { _this.load(); });
} }
SpineWidget.prototype.validateConfig = function (config) { SpineWidget.prototype.validateConfig = function (config) {
@ -7661,6 +7668,8 @@ var spine;
config.animation = widget.getAttribute("data-animation"); config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path")) if (widget.getAttribute("data-images-path"))
config.imagesPath = widget.getAttribute("data-images-path"); config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages"))
config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin")) if (widget.getAttribute("data-skin"))
config.skin = widget.getAttribute("data-skin"); config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop")) if (widget.getAttribute("data-loop"))

File diff suppressed because one or more lines are too long

View File

@ -349,7 +349,7 @@ module spine {
from.timelinesRotation.length = 0; from.timelinesRotation.length = 0;
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if (from.mixingFrom != null) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
} }
this.queue.start(current); this.queue.start(current);
@ -469,7 +469,7 @@ module spine {
entry.trackTime = 0; entry.trackTime = 0;
entry.trackLast = -1; entry.trackLast = -1;
entry.nextTrackLast = -1; entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd; entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;

View File

@ -80,7 +80,13 @@ module spine {
let assets = this.assetManager = new spine.webgl.AssetManager(gl); let assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas); assets.loadText(config.atlas);
assets.loadText(config.json); assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png")); if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
} else {
for (let i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(() => { this.load(); }); requestAnimationFrame(() => { this.load(); });
} }
@ -264,6 +270,7 @@ module spine {
config.json = widget.getAttribute("data-json"); config.json = widget.getAttribute("data-json");
config.animation = widget.getAttribute("data-animation"); config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path")) config.imagesPath = widget.getAttribute("data-images-path"); if (widget.getAttribute("data-images-path")) config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages")) config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin")) config.skin = widget.getAttribute("data-skin"); if (widget.getAttribute("data-skin")) config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop")) config.loop = widget.getAttribute("data-loop") === "true"; if (widget.getAttribute("data-loop")) config.loop = widget.getAttribute("data-loop") === "true";
if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale")); if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale"));
@ -303,6 +310,7 @@ module spine {
atlas: string; atlas: string;
animation: string; animation: string;
imagesPath: string; imagesPath: string;
atlasPages: string[];
skin = "default"; skin = "default";
loop = true; loop = true;
scale = 1.0; scale = 1.0;

View File

@ -69,9 +69,8 @@ namespace Spine.Unity.Examples {
SetXPosition(endX); SetXPosition(endX);
separator.enabled = true; // Enable Separator when hit separator.enabled = true; // Enable Separator when hit
var poleTrack = state.SetAnimation(0, pole, false); var poleTrack = state.SetAnimation(0, pole, false);
float duration = poleTrack.TrackEnd; yield return new WaitForSpineAnimationComplete(poleTrack);
poleTrack.TrackEnd = float.PositiveInfinity; yield return new WaitForSeconds(1f);
yield return new WaitForSeconds(duration + 1f);
} }
} }

View File

@ -1079,7 +1079,7 @@ namespace Spine.Unity.Editor {
AssetDatabase.CreateAsset(skeletonDataAsset, filePath); AssetDatabase.CreateAsset(skeletonDataAsset, filePath);
AssetDatabase.SaveAssets(); AssetDatabase.SaveAssets();
} else { } else {
skeletonDataAsset.Reset(); skeletonDataAsset.Clear();
skeletonDataAsset.GetSkeletonData(true); skeletonDataAsset.GetSkeletonData(true);
} }
@ -1312,16 +1312,16 @@ namespace Spine.Unity.Editor {
anim.skeletonDataAsset = skeletonDataAsset; anim.skeletonDataAsset = skeletonDataAsset;
// Detect "Lit" shader and automatically enable calculateNormals. // Detect "Lit" shader and automatically enable calculateNormals.
bool requiresNormals = false; // bool requiresNormals = false;
foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { // foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
foreach (Material m in atlasAsset.materials) { // foreach (Material m in atlasAsset.materials) {
if (m.shader.name.Contains("Lit")) { // if (m.shader.name.Contains("Lit")) {
requiresNormals = true; // requiresNormals = true;
break; // break;
} // }
} // }
} // }
anim.calculateNormals = requiresNormals; // anim.calculateNormals = requiresNormals;
SkeletonData data = skeletonDataAsset.GetSkeletonData(true); SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
if (data == null) { if (data == null) {
@ -1353,7 +1353,8 @@ namespace Spine.Unity.Editor {
static void EnableTK2D () { static void EnableTK2D () {
bool added = false; bool added = false;
foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
if (group == BuildTargetGroup.Unknown) int gi = (int)group;
if (gi == 15 || gi == 16 || group == BuildTargetGroup.Unknown)
continue; continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
@ -1379,6 +1380,10 @@ namespace Spine.Unity.Editor {
static void DisableTK2D () { static void DisableTK2D () {
bool removed = false; bool removed = false;
foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
int gi = (int)group;
if (gi == 15 || gi == 16 || group == BuildTargetGroup.Unknown)
continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
if (defines.Contains(SPINE_TK2D_DEFINE)) { if (defines.Contains(SPINE_TK2D_DEFINE)) {
removed = true; removed = true;

View File

@ -62,14 +62,13 @@ namespace Spine.Unity {
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately."); Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true; m_WasFired = true;
} else { } else {
// Function normally.
trackEntry.Complete += HandleComplete; trackEntry.Complete += HandleComplete;
} }
} }
#region Reuse #region Reuse
/// <summary> /// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize pressure. /// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary> /// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry) { public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry); SafeSubscribe(trackEntry);

View File

@ -0,0 +1,94 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) 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.
*****************************************************************************/
#if (UNITY_5_0 || UNITY_5_1 || UNITY_5_2 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
#define PREUNITY_5_3
#endif
using UnityEngine;
using System.Collections;
using Spine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its End event.</summary>
public class WaitForSpineTrackEntryEnd : IEnumerator {
bool m_WasFired = false;
public WaitForSpineTrackEntryEnd (Spine.TrackEntry trackEntry) {
#if PREUNITY_5_3
Debug.LogWarning("Unity 5.3 or later is required for Spine Unity custom yield instructions to function correctly.");
#endif
SafeSubscribe(trackEntry);
}
void HandleEnd (TrackEntry trackEntry) {
m_WasFired = true;
}
void SafeSubscribe (Spine.TrackEntry trackEntry) {
if (trackEntry == null) {
// Break immediately if trackEntry is null.
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
trackEntry.End += HandleEnd;
}
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationEnd.</summary>
public WaitForSpineTrackEntryEnd NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry);
return this;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8036c6c2897d2764db92f632d2aef568
timeCreated: 1480672707
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -132,8 +132,8 @@ namespace Spine.Unity.Editor {
skeleton.FlipY = EditorGUILayout.ToggleLeft("skeleton.FlipY", skeleton.FlipY); skeleton.FlipY = EditorGUILayout.ToggleLeft("skeleton.FlipY", skeleton.FlipY);
requireRepaint |= EditorGUI.EndChangeCheck(); requireRepaint |= EditorGUI.EndChangeCheck();
foreach (var t in skeleton.IkConstraints) // foreach (var t in skeleton.IkConstraints)
EditorGUILayout.LabelField(t.Data.Name + " " + t.Mix + " " + t.Target.Data.Name); // EditorGUILayout.LabelField(t.Data.Name + " " + t.Mix + " " + t.Target.Data.Name);
showSlots.target = EditorGUILayout.Foldout(showSlots.target, SlotsRootLabel); showSlots.target = EditorGUILayout.Foldout(showSlots.target, SlotsRootLabel);
if (showSlots.faded > 0) { if (showSlots.faded > 0) {

View File

@ -110,12 +110,13 @@ namespace Spine.Unity {
public Transform boneRoot; public Transform boneRoot;
void Update () { void Update () {
if (boneRoot != null && skeletonRenderer.skeleton != null) { var skeleton = skeletonRenderer.skeleton;
if (boneRoot != null && skeleton != null) {
Vector3 flipScale = Vector3.one; Vector3 flipScale = Vector3.one;
if (skeletonRenderer.skeleton.FlipX) if (skeleton.FlipX)
flipScale.x = -1; flipScale.x = -1;
if (skeletonRenderer.skeleton.FlipY) if (skeleton.FlipY)
flipScale.y = -1; flipScale.y = -1;
boneRoot.localScale = flipScale; boneRoot.localScale = flipScale;

View File

@ -121,7 +121,7 @@ namespace Spine.Unity {
cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0); cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
if (rotation) { if (rotation) {
if (!bone.data.transformMode.InheritsRotation()) { if (bone.data.transformMode.InheritsRotation()) {
cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation); cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
} else { } else {
Vector3 euler = skeletonTransform.rotation.eulerAngles; Vector3 euler = skeletonTransform.rotation.eulerAngles;