mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
Merge branch 'master' into spine-ue4
This commit is contained in:
commit
4c4f2f9539
Binary file not shown.
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8036c6c2897d2764db92f632d2aef568
|
||||
timeCreated: 1480672707
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user