mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-09 08:38:43 +08:00
Merge branch 'master' into 3.6-beta
This commit is contained in:
commit
6fb9a4e991
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;
|
||||
|
||||
@ -126,7 +126,7 @@ public class Atlas {
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -134,7 +134,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
|
||||
@ -160,7 +160,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
|
||||
@ -172,7 +172,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
|
||||
@ -184,7 +184,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
|
||||
@ -220,7 +220,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
|
||||
@ -242,7 +242,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
|
||||
@ -268,7 +268,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
|
||||
@ -291,7 +291,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
|
||||
@ -317,7 +317,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
|
||||
@ -344,7 +344,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
|
||||
@ -393,7 +395,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
|
||||
@ -424,7 +426,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);
|
||||
}
|
||||
});
|
||||
|
||||
@ -437,7 +439,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
|
||||
@ -495,6 +497,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
|
||||
@ -524,7 +527,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
|
||||
@ -552,7 +557,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -593,7 +602,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -655,7 +664,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -669,7 +678,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
|
||||
@ -700,7 +709,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
|
||||
@ -710,7 +719,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);
|
||||
@ -732,7 +741,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);
|
||||
@ -768,7 +777,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);
|
||||
|
||||
@ -185,6 +185,7 @@ public class AnimationState {
|
||||
}
|
||||
}
|
||||
queueEvents(current, animationTime);
|
||||
events.clear();
|
||||
current.nextAnimationLast = animationTime;
|
||||
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.nextTrackLast = from.trackTime;
|
||||
|
||||
@ -330,7 +332,6 @@ public class AnimationState {
|
||||
if (event.time < animationStart) continue; // Discard events outside animation start/end.
|
||||
queue.event(entry, events.get(i));
|
||||
}
|
||||
events.clear();
|
||||
}
|
||||
|
||||
/** 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.
|
||||
|
||||
// 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 +540,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 +753,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.
@ -75,8 +75,8 @@ setup a development environment, follow these steps.
|
||||
* **WebGL**: `tsc -w -p tsconfig.webgl.json`, builds `core/src` and `webgl/src`, outputs `build/spine-webgl.js|d.ts|js.map`
|
||||
* **Canvas**: `tsc -w -p tsconfig.canvas.json`, builds `core/src` and `canvas/src`, outputs `build/spine-canvas.js|d.ts|js.map`
|
||||
* **THREE.JS**: `tsc -w -p tsconfig.threejs.json`, builds `core/src` and `threejs/src`, outputs `build/spine-threejs.js|d.ts|js.map`
|
||||
* **Widget**: `tsc -w -p tsconfig.widget.json`, builds `core/src` and `widget/src`, outputs `build/spine-widget.js|d.ts|js.map`
|
||||
6. Open the `spine-ts` folder in Visual Studio Code. VS Code will use the `tsconfig.json` file all source files from core and all
|
||||
* **Widget**: `tsc -w -p tsconfig.widget.json`, builds `core/src` and `widget/src`, outputs `build/spine-widget.js|d.ts|js.map`
|
||||
6. Open the `spine-ts` folder in Visual Studio Code. VS Code will use the `tsconfig.json` file all source files from core and all
|
||||
backends for your development pleasure. The actual JavaScript output is still created by the command line TypeScript compiler process from the previous step.
|
||||
|
||||
Each backend contains an `example/` folder with an `index.html` file that demonstrates the respective backend. For development, we
|
||||
@ -90,7 +90,7 @@ python -m SimpleHTTPServer
|
||||
Then navigate to `http://localhost:8000/webgl/example`, `http://localhost:8000/canvas/example`, `http://localhost:8000/threejs/example` or `http://localhost:8000/widget/example`
|
||||
|
||||
### Using the Widget
|
||||
To easily display Spine animations on your website, you can use the spine-ts Widget backend.
|
||||
To easily display Spine animations on your website, you can use the spine-ts Widget backend.
|
||||
|
||||
1. Export your Spine animation with a texture atlas and put the resulting `.json`, `.atlas` and `.png` files on your server.
|
||||
2. Copy the `build/spine-widget.js` file to your server and include it on your website `<script src="spine-widget.js"></script>`, adjusting the src to match the location of the file on your server.
|
||||
@ -114,7 +114,7 @@ To specify the configuration of a Spine Widget via HTML, you can use these HTML
|
||||
* `data-fit-to-canvas`: optional, whether to fit the animation to the canvas size or not. Defaults to `true` if omitted, in which case `data-scale`, `data-x` and `data-y` are irrelevant. This setting calculates the setup pose bounding box using the specified skin to center and scale the animation on the canvas.
|
||||
* `data-background-color`: optional, the background color to use. Defaults to `#000000` if omitted.
|
||||
* `data-premultiplied-alpha`: optional, whether the atlas pages use premultiplied alpha or not. Defaults to `false` if omitted.
|
||||
* `data-debug`: optional, whether to show debug information such as bones, attachments, etc. Defaults to `false` if omitted.
|
||||
* `data-debug`: optional, whether to show debug information such as bones, attachments, etc. Defaults to `false` if omitted.
|
||||
|
||||
You can specify these as attribuets on the HTML element like this:
|
||||
|
||||
@ -139,12 +139,12 @@ Then create a new `spine.SpineWidget`, providing a [`SpineWidgetConfiguration`](
|
||||
```JavaScript
|
||||
new spine.SpineWidget("my-widget", {
|
||||
json: "assets/spineboy.json",
|
||||
atlas: "assets/spineboy.atlas",
|
||||
animation: "run",
|
||||
atlas: "assets/spineboy.atlas",
|
||||
animation: "run",
|
||||
backgroundColor: "#000000",
|
||||
success: function (widget) {
|
||||
var animIndex = 0;
|
||||
widget.canvas.onclick = function () {
|
||||
widget.canvas.onclick = function () {
|
||||
animIndex++;
|
||||
let animations = widget.skeleton.data.animations;
|
||||
if (animIndex >= animations.length) animIndex = 0;
|
||||
@ -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"
|
||||
* `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.
|
||||
* `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.
|
||||
* `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`.
|
||||
|
||||
1
spine-ts/build/spine-all.d.ts
vendored
1
spine-ts/build/spine-all.d.ts
vendored
@ -1543,6 +1543,7 @@ declare module spine {
|
||||
atlas: string;
|
||||
animation: string;
|
||||
imagesPath: string;
|
||||
atlasPages: string[];
|
||||
skin: string;
|
||||
loop: boolean;
|
||||
scale: number;
|
||||
|
||||
@ -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;
|
||||
@ -7899,7 +7899,14 @@ var spine;
|
||||
var assets = this.assetManager = new spine.webgl.AssetManager(gl);
|
||||
assets.loadText(config.atlas);
|
||||
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(); });
|
||||
}
|
||||
SpineWidget.prototype.validateConfig = function (config) {
|
||||
@ -8082,6 +8089,8 @@ var spine;
|
||||
config.animation = widget.getAttribute("data-animation");
|
||||
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-loop"))
|
||||
|
||||
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
1
spine-ts/build/spine-widget.d.ts
vendored
1
spine-ts/build/spine-widget.d.ts
vendored
@ -1473,6 +1473,7 @@ declare module spine {
|
||||
atlas: string;
|
||||
animation: string;
|
||||
imagesPath: string;
|
||||
atlasPages: string[];
|
||||
skin: string;
|
||||
loop: boolean;
|
||||
scale: number;
|
||||
|
||||
@ -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;
|
||||
@ -7478,7 +7478,14 @@ var spine;
|
||||
var assets = this.assetManager = new spine.webgl.AssetManager(gl);
|
||||
assets.loadText(config.atlas);
|
||||
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(); });
|
||||
}
|
||||
SpineWidget.prototype.validateConfig = function (config) {
|
||||
@ -7661,6 +7668,8 @@ var spine;
|
||||
config.animation = widget.getAttribute("data-animation");
|
||||
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-loop"))
|
||||
|
||||
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;
|
||||
|
||||
@ -80,7 +80,13 @@ module spine {
|
||||
let assets = this.assetManager = new spine.webgl.AssetManager(gl);
|
||||
assets.loadText(config.atlas);
|
||||
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(); });
|
||||
}
|
||||
|
||||
@ -264,6 +270,7 @@ module spine {
|
||||
config.json = widget.getAttribute("data-json");
|
||||
config.animation = widget.getAttribute("data-animation");
|
||||
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-loop")) config.loop = widget.getAttribute("data-loop") === "true";
|
||||
if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale"));
|
||||
@ -303,6 +310,7 @@ module spine {
|
||||
atlas: string;
|
||||
animation: string;
|
||||
imagesPath: string;
|
||||
atlasPages: string[];
|
||||
skin = "default";
|
||||
loop = true;
|
||||
scale = 1.0;
|
||||
|
||||
@ -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