Merge branch 'master' into spine-ue4

This commit is contained in:
badlogic 2016-12-06 12:55:14 +01:00
commit 4c4f2f9539
32 changed files with 305 additions and 117 deletions

View File

@ -366,7 +366,7 @@ public class AnimationState {
from.timelinesRotation.length = 0;
// 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);
@ -485,7 +485,7 @@ public class AnimationState {
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? int.MAX_VALUE : entry.animationEnd;
entry.trackEnd = int.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;

View File

@ -582,7 +582,7 @@ void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEnt
from->timelinesRotationCount = 0;
/* 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);
@ -715,7 +715,7 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd
entry->trackTime = 0;
entry->trackLast = -1;
entry->nextTrackLast = -1;
entry->trackEnd = loop ? (float)INT_MAX : entry->animationEnd;
entry->trackEnd = (float)INT_MAX;
entry->timeScale = 1;
entry->alpha = 1;

View File

@ -66,11 +66,11 @@ typedef struct {
} 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)++;
if (str->begin == str->end) return;
str->end--;
while (isspace(*str->end) && str->end >= str->begin)
while (isspace((unsigned char)*str->end) && str->end >= str->begin)
str->end--;
str->end++;
}

View File

@ -36,7 +36,7 @@
#include "kvec.h"
typedef struct {
const unsigned char* cursor;
const unsigned char* cursor;
const unsigned char* end;
} _dataInput;
@ -373,7 +373,7 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float mix = readFloat(input);
char bendDirection = readSByte(input);
signed char bendDirection = readSByte(input);
spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection);
if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
}

View File

@ -30,11 +30,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spine {
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 readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
@ -44,6 +43,8 @@ namespace Spine {
private bool animationsChanged;
private float timeScale = 1;
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
public AnimationStateData Data { get { return data; } }
/// <summary>A list of tracks that have animations, which may contain nulls.</summary>
public ExposedList<TrackEntry> Tracks { get { return tracks; } }
@ -58,7 +59,7 @@ namespace Spine {
public AnimationState (AnimationStateData data) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.data = data;
this.queue = new EventQueue(this, HandleAnimationsChanged);
this.queue = new EventQueue(this, HandleAnimationsChanged, trackEntryPool);
}
void HandleAnimationsChanged () {
@ -215,7 +216,7 @@ namespace Spine {
float alpha = from.alpha * entry.mixAlpha * (1 - mix);
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;
for (int i = 0; i < timelineCount; i++) {
@ -389,7 +390,7 @@ namespace Spine {
from.timelinesRotation.Clear();
// 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);
@ -526,32 +527,32 @@ namespace Spine {
/// <param name="last">May be null.</param>
private TrackEntry NewTrackEntry (int trackIndex, Animation animation, bool loop, TrackEntry last) {
return new TrackEntry {
trackIndex = trackIndex,
animation = animation,
loop = loop,
TrackEntry entry = trackEntryPool.Obtain(); // Pooling
entry.trackIndex = trackIndex;
entry.animation = animation;
entry.loop = loop;
eventThreshold = 0,
attachmentThreshold = 0,
drawOrderThreshold = 0,
entry.eventThreshold = 0;
entry.attachmentThreshold = 0;
entry.drawOrderThreshold = 0;
animationStart = 0,
animationEnd = animation.duration,
animationLast = -1,
nextAnimationLast = -1,
entry.animationStart = 0;
entry.animationEnd = animation.Duration;
entry.animationLast = -1;
entry.nextAnimationLast = -1;
delay = 0,
trackTime = 0,
trackLast = -1,
nextTrackLast = -1,
trackEnd = loop ? int.MaxValue : animation.duration,
timeScale = 1,
entry.delay = 0;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = float.MaxValue; // loop ? float.MaxValue : animation.Duration;
entry.timeScale = 1;
alpha = 1,
mixAlpha = 1,
mixTime = 0,
mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation),
};
entry.alpha = 1;
entry.mixAlpha = 1;
entry.mixTime = 0;
entry.mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation);
return entry;
}
private void DisposeNext (TrackEntry entry) {
@ -627,7 +628,7 @@ namespace Spine {
}
override public String ToString () {
var buffer = new StringBuilder();
var buffer = new System.Text.StringBuilder();
for (int i = 0, n = tracks.Count; i < n; i++) {
TrackEntry entry = tracks.Items[i];
if (entry == null) continue;
@ -646,7 +647,7 @@ namespace Spine {
}
/// <summary>State for the playback of an animation.</summary>
public class TrackEntry {
public class TrackEntry : Pool<TrackEntry>.IPoolable {
internal Animation animation;
internal TrackEntry next, mixingFrom;
@ -657,9 +658,25 @@ namespace Spine {
internal float animationStart, animationEnd, animationLast, nextAnimationLast;
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
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>();
// 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>
public int TrackIndex { get { return trackIndex; } }
@ -824,11 +841,13 @@ namespace Spine {
public bool drainDisabled;
private readonly AnimationState state;
private readonly Pool<TrackEntry> trackEntryPool;
public event Action AnimationsChanged;
public EventQueue (AnimationState state, Action HandleAnimationsChanged) {
public EventQueue (AnimationState state, Action HandleAnimationsChanged, Pool<TrackEntry> trackEntryPool) {
this.state = state;
this.AnimationsChanged += HandleAnimationsChanged;
this.trackEntryPool = trackEntryPool;
}
struct EventQueueEntry {
@ -901,6 +920,7 @@ namespace Spine {
case EventType.Dispose:
trackEntry.OnDispose();
state.OnDispose(trackEntry);
trackEntryPool.Free(trackEntry); // Pooling
break;
case EventType.Complete:
trackEntry.OnComplete();
@ -921,4 +941,57 @@ namespace Spine {
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

@ -129,7 +129,7 @@ public class AnimationStateTests {
expect(0, "end", 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);
setup("1/60 time step, dispose queued", // 2
@ -155,7 +155,7 @@ public class AnimationStateTests {
state.addAnimation(0, "events1", false, 0);
state.addAnimation(0, "events0", 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);
setup("30 time step", // 3
@ -167,7 +167,7 @@ public class AnimationStateTests {
expect(0, "end", 30, 60), //
expect(0, "dispose", 30, 60) //
);
state.setAnimation(0, "events0", false);
state.setAnimation(0, "events0", false).setTrackEnd(1);
run(30, 1000, null);
setup("1 time step", // 4
@ -179,7 +179,7 @@ public class AnimationStateTests {
expect(0, "end", 1, 2), //
expect(0, "dispose", 1, 2) //
);
state.setAnimation(0, "events0", false);
state.setAnimation(0, "events0", false).setTrackEnd(1);
run(1, 1.01f, null);
setup("interrupt", // 5
@ -215,7 +215,7 @@ public class AnimationStateTests {
);
state.setAnimation(0, "events0", false);
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);
setup("interrupt with delay", // 6
@ -237,7 +237,7 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 1.6f) //
);
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);
setup("interrupt with delay and mix time", // 7
@ -263,7 +263,7 @@ public class AnimationStateTests {
);
stateData.setMix("events0", "events1", 0.7f);
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);
setup("animation 0 events do not fire during mix", // 8
@ -286,7 +286,7 @@ public class AnimationStateTests {
);
stateData.setDefaultMix(0.7f);
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);
setup("event threshold, some animation 0 events fire during mix", // 9
@ -312,7 +312,7 @@ public class AnimationStateTests {
);
stateData.setMix("events0", "events1", 0.7f);
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);
setup("event threshold, all animation 0 events fire during mix", // 10
@ -339,7 +339,9 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 1.9f) //
);
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);
setup("looping", // 11
@ -388,7 +390,7 @@ public class AnimationStateTests {
expect(1, "dispose", 1, 3.1f) //
);
state.setAnimation(0, "events0", false);
state.addAnimation(0, "events1", false, 2);
state.addAnimation(0, "events1", false, 2).setTrackEnd(1);
run(0.1f, 4f, null);
setup("interrupt animation after first loop complete", // 13
@ -419,7 +421,7 @@ public class AnimationStateTests {
state.setAnimation(0, "events0", true);
run(0.1f, 6, new TestListener() {
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);
}
});
@ -432,7 +434,7 @@ public class AnimationStateTests {
expect(0, "end", 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);
setup("end time beyond non-looping animation duration", // 15
@ -490,6 +492,7 @@ public class AnimationStateTests {
entry.setAnimationStart(0.2f);
entry.setAnimationLast(0.2f);
entry.setAnimationEnd(0.8f);
entry.setTrackEnd(1);
run(0.1f, 1.8f, null);
setup("mix out looping with animation start and end", // 19
@ -519,7 +522,9 @@ public class AnimationStateTests {
entry.setAnimationLast(0.2f);
entry.setAnimationEnd(0.8f);
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);
setup("setAnimation with track entry mix", // 20
@ -547,7 +552,11 @@ public class AnimationStateTests {
state.setAnimation(0, "events0", true);
run(0.1f, 1000, new TestListener() {
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);
}
}
});
@ -588,7 +597,7 @@ public class AnimationStateTests {
public void frame (float time) {
if (MathUtils.isEqual(time, 0.8f)) {
state.setAnimation(0, "events0", false); // First should be ignored.
state.setAnimation(0, "events2", false);
state.setAnimation(0, "events2", false).setTrackEnd(1);
}
}
});
@ -650,7 +659,7 @@ public class AnimationStateTests {
}
if (MathUtils.isEqual(time, 0.4f)) {
state.setAnimation(0, "events1", false); // First should be ignored.
state.setAnimation(0, "events0", false);
state.setAnimation(0, "events0", false).setTrackEnd(1);
}
}
});
@ -664,7 +673,7 @@ public class AnimationStateTests {
expect(0, "end", 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);
setup("setAnimation during AnimationStateListener"); // 24
@ -695,7 +704,7 @@ public class AnimationStateTests {
});
state.addAnimation(0, "events0", 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);
setup("clearTrack", // 25
@ -705,7 +714,7 @@ public class AnimationStateTests {
expect(0, "end", 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() {
public void frame (float time) {
if (MathUtils.isEqual(time, 0.7f)) state.clearTrack(0);
@ -727,7 +736,7 @@ public class AnimationStateTests {
expect(-1, "end", 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() {
public void frame (float time) {
if (MathUtils.isEqual(time, 0.7f)) state.setEmptyAnimation(0, 0);
@ -763,7 +772,7 @@ public class AnimationStateTests {
});
state.addAnimation(0, "events0", 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);
if (counter.get() != 15082016) {
log("TEST 26 FAILED! " + counter);

View File

@ -385,7 +385,7 @@ public class AnimationState {
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 (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);
@ -539,7 +539,7 @@ public class AnimationState {
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Integer.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Float.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;
@ -752,10 +752,10 @@ public class AnimationState {
this.trackTime = trackTime;
}
/** The track time in seconds when this animation will be removed from the track. Defaults to the animation
* {@link Animation#duration} for non-looping animations and the highest float possible for looping animations. If the track
* end time is reached, no other animations are queued for playback, and mixing from any previous animations is complete,
* then the properties keyed by the animation are set to the setup pose and the track is cleared.
/** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float
* value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time
* is reached, no other animations are queued for playback, and mixing from any previous animations is complete, then the
* properties keyed by the animation are set to the setup pose and the track is cleared.
* <p>
* 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. */

View File

@ -541,7 +541,7 @@ function AnimationState:setCurrent (index, current, interrupt)
from.timelinesRotation = {};
-- 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
queue:start(current)
@ -665,11 +665,7 @@ function AnimationState:trackEntry (trackIndex, animation, loop, last)
entry.trackTime = 0
entry.trackLast = -1
entry.nextTrackLast = -1
if loop then
entry.trackEnd = 999999999
else
entry.trackEnd = entry.animationEnd
end
entry.trackEnd = 999999999
entry.timeScale = 1
entry.alpha = 1

View File

@ -1568,7 +1568,7 @@ var spine;
current.mixingFrom = from;
current.mixTime = 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);
}
this.queue.start(current);
@ -1680,7 +1680,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;
entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -1568,7 +1568,7 @@ var spine;
current.mixingFrom = from;
current.mixTime = 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);
}
this.queue.start(current);
@ -1680,7 +1680,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 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.mixTime = 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);
}
this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 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.mixTime = 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);
}
this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 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.mixTime = 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);
}
this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 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.mixTime = 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);
}
this.queue.start(current);
@ -1333,7 +1333,7 @@ var spine;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;
entry.mixAlpha = 1;

File diff suppressed because one or more lines are too long

View File

@ -349,7 +349,7 @@ module spine {
from.timelinesRotation.length = 0;
// 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);
@ -469,7 +469,7 @@ module spine {
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = loop ? Number.MAX_VALUE : entry.animationEnd;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;

View File

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

View File

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

View File

@ -62,14 +62,13 @@ namespace Spine.Unity {
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
// Function normally.
trackEntry.Complete += HandleComplete;
}
}
#region Reuse
/// <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>
public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry 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);
requireRepaint |= EditorGUI.EndChangeCheck();
foreach (var t in skeleton.IkConstraints)
EditorGUILayout.LabelField(t.Data.Name + " " + t.Mix + " " + t.Target.Data.Name);
// foreach (var t in skeleton.IkConstraints)
// EditorGUILayout.LabelField(t.Data.Name + " " + t.Mix + " " + t.Target.Data.Name);
showSlots.target = EditorGUILayout.Foldout(showSlots.target, SlotsRootLabel);
if (showSlots.faded > 0) {

View File

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

View File

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