mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-10 17:18:44 +08:00
Fixes for AnimationState event order.
This commit is contained in:
parent
534ffc8d1d
commit
9171f79cc2
@ -1,5 +1,5 @@
|
||||
{
|
||||
"skeleton": { "hash": "NG9aJneROk8CsMAugcCOiMeXbGA", "spine": "Dev", "width": 0, "height": 0 },
|
||||
"skeleton": { "hash": "Qw/GA2Pge/zEE/8xq3vX9lpp4Qg", "spine": "3.0.00", "width": 0, "height": 0 },
|
||||
"bones": [
|
||||
{ "name": "root" }
|
||||
],
|
||||
@ -7,7 +7,14 @@
|
||||
"event": {}
|
||||
},
|
||||
"animations": {
|
||||
"events": {
|
||||
"events1": {
|
||||
"events": [
|
||||
{ "time": 0, "name": "event", "string": "0" },
|
||||
{ "time": 0.4666, "name": "event", "string": "14" },
|
||||
{ "time": 1, "name": "event", "string": "30" }
|
||||
]
|
||||
},
|
||||
"events2": {
|
||||
"events": [
|
||||
{ "time": 0, "name": "event", "string": "0" },
|
||||
{ "time": 0.4666, "name": "event", "string": "14" },
|
||||
|
||||
Binary file not shown.
@ -35,19 +35,20 @@ import com.badlogic.gdx.Files.FileType;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglFileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
||||
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
|
||||
public class AnimationStateTest {
|
||||
final SkeletonJson json = new SkeletonJson(new AttachmentLoader() {
|
||||
public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
|
||||
public RegionAttachment newRegionAttachment (Skin skin, String name, String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public RegionAttachment newRegionAttachment (Skin skin, String name, String path) {
|
||||
public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -60,154 +61,274 @@ public class AnimationStateTest {
|
||||
}
|
||||
});
|
||||
|
||||
AnimationStateListener stateListener = new AnimationStateListener() {
|
||||
public void start (int trackIndex) {
|
||||
actual.add(new Result("start", null));
|
||||
final AnimationStateListener stateListener = new AnimationStateListener() {
|
||||
public void start (TrackEntry entry) {
|
||||
add(actual("start", entry));
|
||||
}
|
||||
|
||||
public void event (int trackIndex, Event event) {
|
||||
actual.add(new Result("event", event.getString()));
|
||||
public void event (TrackEntry entry, Event event) {
|
||||
add(actual("event " + event.getString(), entry));
|
||||
}
|
||||
|
||||
public void complete (int trackIndex, int loopCount) {
|
||||
actual.add(new Result("complete", null));
|
||||
public void interrupt (TrackEntry entry) {
|
||||
add(actual("interrupt", entry));
|
||||
}
|
||||
|
||||
public void end (int trackIndex) {
|
||||
actual.add(new Result("end", null));
|
||||
public void complete (TrackEntry entry, int loopCount) {
|
||||
add(actual("complete " + loopCount, entry));
|
||||
}
|
||||
|
||||
public void end (TrackEntry entry) {
|
||||
add(actual("end", entry));
|
||||
}
|
||||
|
||||
private void add (Result result) {
|
||||
String error = "PASS";
|
||||
if (actual.size >= expected.size) {
|
||||
error = "FAIL: <none>";
|
||||
fail = true;
|
||||
} else if (!expected.get(actual.size).equals(result)) {
|
||||
error = "FAIL: " + expected.get(actual.size);
|
||||
fail = true;
|
||||
}
|
||||
buffer.append(result.toString());
|
||||
buffer.append(error);
|
||||
buffer.append('\n');
|
||||
actual.add(result);
|
||||
}
|
||||
};
|
||||
|
||||
final SkeletonData skeletonData;
|
||||
final AnimationStateData stateData;
|
||||
final Array<Result> actual = new Array();
|
||||
final Array<Result> expected = new Array();
|
||||
final StringBuilder buffer = new StringBuilder(512);
|
||||
|
||||
public AnimationStateTest () {
|
||||
AnimationState state;
|
||||
float time = 0;
|
||||
boolean fail;
|
||||
int test;
|
||||
|
||||
AnimationStateTest () {
|
||||
skeletonData = json.readSkeletonData(new LwjglFileHandle("test/test.json", FileType.Internal));
|
||||
stateData = new AnimationStateData(skeletonData);
|
||||
|
||||
AnimationState state;
|
||||
|
||||
state = newState();
|
||||
state.setAnimation(0, "events", false);
|
||||
test(state, 1 / 60f, 1000, //
|
||||
new Result("start", null), //
|
||||
new Result("event", "0"), //
|
||||
new Result("event", "14"), //
|
||||
new Result("event", "30"), //
|
||||
new Result("complete", null), //
|
||||
new Result("end", null) //
|
||||
setup( // 1
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.5f, 0.5f), //
|
||||
expect("event 30", 0, 1, 1), //
|
||||
expect("complete 1", 0, 1, 1), //
|
||||
expect("end", 0, 1, 1.1f) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
run(0.1f, 1000);
|
||||
|
||||
state = newState();
|
||||
state.setAnimation(0, "events", false);
|
||||
test(state, 30, 1000, //
|
||||
new Result("start", null), //
|
||||
new Result("event", "0"), //
|
||||
new Result("event", "14"), //
|
||||
new Result("event", "30"), //
|
||||
new Result("complete", null), //
|
||||
new Result("end", null) //
|
||||
setup( // 2
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.467f, 0.467f), //
|
||||
expect("event 30", 0, 1.017f, 1.017f), //
|
||||
expect("complete 1", 0, 1.017f, 1.017f), //
|
||||
expect("end", 0, 1.017f, 1.033f) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
run(1 / 60f, 1000);
|
||||
|
||||
state = newState();
|
||||
state.setAnimation(0, "events", false);
|
||||
test(state, 1, 1.01f, //
|
||||
new Result("start", null), //
|
||||
new Result("event", "0"), //
|
||||
new Result("event", "14"), //
|
||||
new Result("event", "30"), //
|
||||
new Result("complete", null), //
|
||||
new Result("end", null) //
|
||||
setup( // 3
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 30, 30), //
|
||||
expect("event 30", 0, 30, 30), //
|
||||
expect("complete 1", 0, 30, 30), //
|
||||
expect("end", 0, 30, 60) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
run(30, 1000);
|
||||
|
||||
state = newState();
|
||||
state.setAnimation(0, "events", false);
|
||||
state.addAnimation(0, "events", false, 0);
|
||||
test(state, 0.1f, 3f, //
|
||||
new Result("start", null), //
|
||||
new Result("event", "0"), //
|
||||
new Result("event", "14"), //
|
||||
new Result("event", "30"), //
|
||||
new Result("complete", null), //
|
||||
new Result("end", null), //
|
||||
new Result("start", null), //
|
||||
new Result("event", "0"), //
|
||||
new Result("event", "14"), //
|
||||
new Result("event", "30"), //
|
||||
new Result("complete", null), //
|
||||
new Result("end", null) //
|
||||
setup( // 4
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 1, 1), //
|
||||
expect("event 30", 0, 1, 1), //
|
||||
expect("complete 1", 0, 1, 1), //
|
||||
expect("end", 0, 1, 2) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
run(1, 1.01f);
|
||||
|
||||
setup( // 5
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.5f, 0.5f), //
|
||||
expect("event 30", 0, 1, 1), //
|
||||
expect("complete 1", 0, 1, 1), //
|
||||
expect("event 0", 0, 1, 1), //
|
||||
expect("event 14", 0, 1.5f, 1.5f), //
|
||||
expect("event 30", 0, 2, 2), //
|
||||
expect("complete 2", 0, 2, 2), //
|
||||
expect("event 0", 0, 2, 2) //
|
||||
);
|
||||
state.setAnimation(0, "events1", true);
|
||||
run(0.1f, 2.3f);
|
||||
|
||||
setup( // 6
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.5f, 0.5f), //
|
||||
expect("event 30", 0, 1, 1), //
|
||||
expect("complete 1", 0, 1, 1), //
|
||||
|
||||
expect("start", 1, 0.1f, 1.1f), //
|
||||
|
||||
expect("interrupt", 0, 1.1f, 1.1f), //
|
||||
expect("end", 0, 1.1f, 1.1f), //
|
||||
|
||||
expect("event 0", 1, 0.1f, 1.1f), //
|
||||
expect("event 14", 1, 0.5f, 1.5f), //
|
||||
expect("event 30", 1, 1, 2), //
|
||||
expect("complete 1", 1, 1, 2), //
|
||||
|
||||
expect("start", 0, 0.1f, 2.1f), //
|
||||
|
||||
expect("interrupt", 1, 1.1f, 2.1f), //
|
||||
expect("end", 1, 1.1f, 2.1f), //
|
||||
|
||||
expect("event 0", 0, 0.1f, 2.1f), //
|
||||
expect("event 14", 0, 0.5f, 2.5f), //
|
||||
expect("event 30", 0, 1, 3), //
|
||||
expect("complete 1", 0, 1, 3), //
|
||||
expect("end", 0, 1, 3.1f) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
state.addAnimation(0, "events2", false, 0);
|
||||
state.addAnimation(0, "events1", false, 0);
|
||||
run(0.1f, 4f);
|
||||
|
||||
setup( // 7
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.5f, 0.5f), //
|
||||
|
||||
expect("start", 1, 0.1f, 0.6f), //
|
||||
|
||||
expect("interrupt", 0, 0.6f, 0.6f), //
|
||||
expect("end", 0, 0.6f, 0.6f), //
|
||||
|
||||
expect("event 0", 1, 0.1f, 0.6f), //
|
||||
expect("event 14", 1, 0.5f, 1.0f), //
|
||||
expect("event 30", 1, 1, 1.5f), //
|
||||
expect("complete 1", 1, 1, 1.5f), //
|
||||
expect("end", 1, 1, 1.6f) //
|
||||
);
|
||||
state.setAnimation(0, "events1", false);
|
||||
state.addAnimation(0, "events2", false, 0.5f);
|
||||
run(0.1f, 1000);
|
||||
|
||||
setup( // 8
|
||||
expect("start", 0, 0, 0), //
|
||||
expect("event 0", 0, 0, 0), //
|
||||
expect("event 14", 0, 0.5f, 0.5f), //
|
||||
|
||||
expect("start", 1, 0.1f, 1), //
|
||||
|
||||
expect("interrupt", 0, 1, 1), //
|
||||
expect("event 30", 0, 1, 1), //
|
||||
expect("complete 1", 0, 1, 1), //
|
||||
expect("event 0", 0, 1, 1), //
|
||||
|
||||
expect("event 0", 1, 0.1f, 1), //
|
||||
expect("event 14", 1, 0.5f, 1.4f), //
|
||||
|
||||
expect("event 14", 0, 1.5f, 1.5f), //
|
||||
expect("end", 0, 1.6f, 1.6f), //
|
||||
|
||||
expect("event 30", 1, 1, 1.9f), //
|
||||
expect("complete 1", 1, 1, 1.9f), //
|
||||
expect("end", 1, 1, 2) //
|
||||
);
|
||||
stateData.setMix("events1", "events2", 0.7f);
|
||||
state.setAnimation(0, "events1", true);
|
||||
state.addAnimation(0, "events2", false, 0.9f);
|
||||
run(0.1f, 1000);
|
||||
|
||||
System.out.println("AnimationState tests passed.");
|
||||
}
|
||||
|
||||
private AnimationState newState () {
|
||||
AnimationState state = new AnimationState(stateData);
|
||||
void setup (Result... expectedArray) {
|
||||
test++;
|
||||
expected.addAll(expectedArray);
|
||||
state = new AnimationState(stateData);
|
||||
state.addListener(stateListener);
|
||||
return state;
|
||||
time = 0;
|
||||
fail = false;
|
||||
buffer.setLength(0);
|
||||
buffer.append(String.format("%-12s%-8s%-8s%-8s%s\n", "", "anim", "track", "total", "result"));
|
||||
}
|
||||
|
||||
private void test (AnimationState state, float incr, float endTime, Result... expectedArray) {
|
||||
Array expected = new Array(expectedArray);
|
||||
|
||||
void run (float incr, float endTime) {
|
||||
Skeleton skeleton = new Skeleton(skeletonData);
|
||||
|
||||
for (int i = 0; i < endTime; i++) {
|
||||
state.apply(skeleton);
|
||||
while (time < endTime) {
|
||||
time += incr;
|
||||
skeleton.update(incr);
|
||||
state.update(incr);
|
||||
state.apply(skeleton);
|
||||
}
|
||||
|
||||
if (expected.equals(actual)) {
|
||||
actual.clear();
|
||||
return;
|
||||
actual.clear();
|
||||
expected.clear();
|
||||
if (fail) {
|
||||
System.out.println("Test failed: " + test);
|
||||
System.out.println(buffer);
|
||||
System.exit(0);
|
||||
}
|
||||
int i = 0;
|
||||
for (int n = expected.size; i < n; i++) {
|
||||
System.out.print(expected.get(i) + " == " + (i < actual.size ? actual.get(i) : ""));
|
||||
if (i >= actual.size || !actual.get(i).equals(expected.get(i)))
|
||||
System.out.println(" <- FAIL");
|
||||
else
|
||||
System.out.println();
|
||||
}
|
||||
for (int n = actual.size; i < n; i++)
|
||||
System.out.print(" == " + actual.get(i) + " <- FAIL");
|
||||
System.exit(0);
|
||||
System.out.println(buffer);
|
||||
}
|
||||
|
||||
static public class Result {
|
||||
String eventName;
|
||||
String payload;
|
||||
Result expect (String name, int animationIndex, float trackTime, float totalTime) {
|
||||
Result result = new Result();
|
||||
result.name = name;
|
||||
result.animationIndex = animationIndex;
|
||||
result.trackTime = trackTime;
|
||||
result.totalTime = totalTime;
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result (String eventName, String payload) {
|
||||
this.eventName = eventName;
|
||||
this.payload = payload;
|
||||
}
|
||||
Result actual (String name, TrackEntry entry) {
|
||||
Result result = new Result();
|
||||
result.name = name;
|
||||
result.animationIndex = skeletonData.getAnimations().indexOf(entry.animation, true);
|
||||
result.trackTime = Math.round(entry.time * 1000) / 1000f;
|
||||
result.totalTime = Math.round(time * 1000) / 1000f;
|
||||
return result;
|
||||
}
|
||||
|
||||
class Result {
|
||||
String name;
|
||||
int animationIndex;
|
||||
float trackTime, totalTime;
|
||||
|
||||
public int hashCode () {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((eventName == null) ? 0 : eventName.hashCode());
|
||||
result = prime * result + ((payload == null) ? 0 : payload.hashCode());
|
||||
int result = 31 + animationIndex;
|
||||
result = 31 * result + name.hashCode();
|
||||
result = 31 * result + Float.floatToIntBits(totalTime);
|
||||
result = 31 * result + Float.floatToIntBits(trackTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean equals (Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
Result other = (Result)obj;
|
||||
if (eventName == null) {
|
||||
if (other.eventName != null) return false;
|
||||
} else if (!eventName.equals(other.eventName)) return false;
|
||||
if (payload == null) {
|
||||
if (other.payload != null) return false;
|
||||
} else if (!payload.equals(other.payload)) return false;
|
||||
if (animationIndex != other.animationIndex) return false;
|
||||
if (!name.equals(other.name)) return false;
|
||||
if (totalTime != other.totalTime) return false;
|
||||
if (trackTime != other.trackTime) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return "[" + eventName + ", " + payload + "]";
|
||||
return String.format("%-12s%-8s%-8s%-8s", name, "" + animationIndex, "" + trackTime, "" + totalTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public void main (String[] args) throws Exception {
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
||||
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
@ -82,24 +83,28 @@ public class SimpleTest2 extends ApplicationAdapter {
|
||||
state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc).
|
||||
state.setTimeScale(0.3f); // Slow all animations down to 30% speed.
|
||||
state.addListener(new AnimationStateListener() {
|
||||
public void event (int trackIndex, Event event) {
|
||||
System.out.println(trackIndex + " event: " + state.getCurrent(trackIndex) + ", " + event.getData().getName() + ", "
|
||||
+ event.getInt());
|
||||
public void event (TrackEntry entry, Event event) {
|
||||
System.out
|
||||
.println(entry.getTrackIndex() + " event: " + entry + ", " + event.getData().getName() + ", " + event.getInt());
|
||||
}
|
||||
|
||||
public void complete (int trackIndex, int loopCount) {
|
||||
System.out.println(trackIndex + " complete: " + state.getCurrent(trackIndex) + ", " + loopCount);
|
||||
public void complete (TrackEntry entry, int loopCount) {
|
||||
System.out.println(entry.getTrackIndex() + " complete: " + entry + ", " + loopCount);
|
||||
}
|
||||
|
||||
public void start (int trackIndex) {
|
||||
System.out.println(trackIndex + " start: " + state.getCurrent(trackIndex));
|
||||
public void interrupt (TrackEntry entry) {
|
||||
System.out.println(entry.getTrackIndex() + " interrupt: " + entry);
|
||||
}
|
||||
|
||||
public void end (int trackIndex) {
|
||||
System.out.println(trackIndex + " end: " + state.getCurrent(trackIndex));
|
||||
public void start (TrackEntry entry) {
|
||||
System.out.println(entry.getTrackIndex() + " start: " + entry);
|
||||
}
|
||||
|
||||
public void end (TrackEntry entry) {
|
||||
System.out.println(entry.getTrackIndex() + " end: " + entry);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Set animation on track 0.
|
||||
state.setAnimation(0, "run", true);
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
import com.badlogic.gdx.utils.Pool.Poolable;
|
||||
|
||||
@ -40,16 +41,17 @@ public class AnimationState {
|
||||
private AnimationStateData data;
|
||||
private Array<TrackEntry> tracks = new Array();
|
||||
private final Array<Event> events = new Array();
|
||||
private final Array<AnimationStateListener> listeners = new Array();
|
||||
private final EventQueue queue = new EventQueue();
|
||||
final Array<AnimationStateListener> listeners = new Array();
|
||||
private float timeScale = 1;
|
||||
|
||||
private Pool<TrackEntry> trackEntryPool = new Pool() {
|
||||
final Pool<TrackEntry> trackEntryPool = new Pool() {
|
||||
protected Object newObject () {
|
||||
return new TrackEntry();
|
||||
}
|
||||
};
|
||||
|
||||
/** Creates an uninitialized AnimationState. The animation state data must be set. */
|
||||
/** Creates an uninitialized AnimationState. The animation state data must be set before use. */
|
||||
public AnimationState () {
|
||||
}
|
||||
|
||||
@ -64,16 +66,19 @@ public class AnimationState {
|
||||
TrackEntry current = tracks.get(i);
|
||||
if (current == null) continue;
|
||||
|
||||
float currentDelta = delta * current.timeScale;
|
||||
|
||||
TrackEntry next = current.next;
|
||||
if (next != null) {
|
||||
// When the next entry's delay is passed, change to it.
|
||||
float nextTime = current.lastTime - next.delay;
|
||||
if (nextTime >= 0) {
|
||||
float nextDelta = delta * next.timeScale;
|
||||
next.time = nextTime + nextDelta; // For start event to see correct time.
|
||||
current.time += delta * current.timeScale; // For end event to see correct time.
|
||||
next.time = nextTime + delta * next.timeScale;
|
||||
current.time += currentDelta;
|
||||
setCurrent(i, next);
|
||||
next.time -= nextDelta; // Prevent increasing time twice, below.
|
||||
current = next;
|
||||
queue.drain();
|
||||
if (next.previous != null) next.mixTime += currentDelta;
|
||||
continue;
|
||||
}
|
||||
} else if (!current.loop && current.lastTime >= current.endTime) {
|
||||
// End non-looping animation when it reaches its end time and there is no next entry.
|
||||
@ -81,7 +86,7 @@ public class AnimationState {
|
||||
continue;
|
||||
}
|
||||
|
||||
current.time += delta * current.timeScale;
|
||||
current.time += currentDelta;
|
||||
if (current.previous != null) {
|
||||
float previousDelta = delta * current.previous.timeScale;
|
||||
current.previous.time += previousDelta;
|
||||
@ -92,54 +97,63 @@ public class AnimationState {
|
||||
|
||||
public void apply (Skeleton skeleton) {
|
||||
Array<Event> events = this.events;
|
||||
int listenerCount = listeners.size;
|
||||
|
||||
for (int i = 0; i < tracks.size; i++) {
|
||||
TrackEntry current = tracks.get(i);
|
||||
if (current == null) continue;
|
||||
|
||||
events.size = 0;
|
||||
|
||||
float time = current.time;
|
||||
float lastTime = current.lastTime;
|
||||
float endTime = current.endTime;
|
||||
boolean loop = current.loop;
|
||||
if (!loop && time > endTime) time = endTime;
|
||||
|
||||
float alpha = current.mix;
|
||||
TrackEntry previous = current.previous;
|
||||
if (previous == null)
|
||||
current.animation.mix(skeleton, lastTime, time, loop, events, current.mix);
|
||||
else {
|
||||
if (previous != null) {
|
||||
float previousTime = previous.time;
|
||||
if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
|
||||
previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);
|
||||
previous.animation.mix(skeleton, previous.lastTime, previousTime, previous.loop, events, previous.mix);
|
||||
queueEvents(previous, previous.lastTime, previousTime, previous.endTime);
|
||||
previous.lastTime = previousTime;
|
||||
|
||||
float alpha = current.mixTime / current.mixDuration * current.mix;
|
||||
alpha *= current.mixTime / current.mixDuration;
|
||||
if (alpha >= 1) {
|
||||
alpha = 1;
|
||||
trackEntryPool.free(previous);
|
||||
queue.queueEnd(current.previous);
|
||||
current.previous = null;
|
||||
}
|
||||
current.animation.mix(skeleton, lastTime, time, loop, events, alpha);
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = events.size; ii < nn; ii++) {
|
||||
Event event = events.get(ii);
|
||||
if (current.listener != null) current.listener.event(i, event);
|
||||
for (int iii = 0; iii < listenerCount; iii++)
|
||||
listeners.get(iii).event(i, event);
|
||||
}
|
||||
|
||||
// Check if completed the animation or a loop iteration.
|
||||
if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) {
|
||||
int count = (int)(time / endTime);
|
||||
if (current.listener != null) current.listener.complete(i, count);
|
||||
for (int ii = 0, nn = listeners.size; ii < nn; ii++)
|
||||
listeners.get(ii).complete(i, count);
|
||||
}
|
||||
current.animation.mix(skeleton, lastTime, time, loop, events, alpha);
|
||||
queueEvents(current, lastTime, time, endTime);
|
||||
|
||||
current.lastTime = current.time;
|
||||
}
|
||||
|
||||
queue.drain();
|
||||
}
|
||||
|
||||
private void queueEvents (TrackEntry entry, float lastTime, float time, float endTime) {
|
||||
Array<Event> events = this.events;
|
||||
int n = events.size;
|
||||
|
||||
// Queue events before complete.
|
||||
float lastTimeWrapped = lastTime % endTime;
|
||||
int i = 0;
|
||||
for (; i < n; i++) {
|
||||
Event event = events.get(i);
|
||||
if (events.get(i).time < lastTimeWrapped) break;
|
||||
queue.queueEvent(entry, event);
|
||||
}
|
||||
|
||||
// Queue complete if completed the animation or a loop iteration.
|
||||
if (entry.loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime))
|
||||
queue.queueComplete(entry, (int)(time / endTime));
|
||||
|
||||
// Queue events after complete.
|
||||
for (; i < n; i++)
|
||||
queue.queueEvent(entry, events.get(i));
|
||||
events.clear();
|
||||
}
|
||||
|
||||
public void clearTracks () {
|
||||
@ -153,9 +167,9 @@ public class AnimationState {
|
||||
TrackEntry current = tracks.get(trackIndex);
|
||||
if (current == null) return;
|
||||
|
||||
if (current.listener != null) current.listener.end(trackIndex);
|
||||
if (current.listener != null) current.listener.end(current);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).end(trackIndex);
|
||||
listeners.get(i).end(current);
|
||||
|
||||
tracks.set(trackIndex, null);
|
||||
|
||||
@ -180,13 +194,16 @@ public class AnimationState {
|
||||
|
||||
private void setCurrent (int index, TrackEntry entry) {
|
||||
TrackEntry current = expandToIndex(index);
|
||||
|
||||
tracks.set(index, entry);
|
||||
|
||||
queue.fireStart(entry);
|
||||
|
||||
if (current != null) {
|
||||
TrackEntry previous = current.previous;
|
||||
current.previous = null;
|
||||
|
||||
if (current.listener != null) current.listener.end(index);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).end(index);
|
||||
queue.fireInterrupt(current);
|
||||
|
||||
entry.mixDuration = data.getMix(current.animation, entry.animation);
|
||||
if (entry.mixDuration > 0) {
|
||||
@ -195,19 +212,14 @@ public class AnimationState {
|
||||
if (previous != null && current.mixTime / current.mixDuration < 0.5f) {
|
||||
entry.previous = previous;
|
||||
previous = current;
|
||||
} else
|
||||
} else {
|
||||
entry.previous = current;
|
||||
}
|
||||
} else
|
||||
trackEntryPool.free(current);
|
||||
queue.fireEnd(current);
|
||||
|
||||
if (previous != null) trackEntryPool.free(previous);
|
||||
if (previous != null) queue.fireEnd(previous);
|
||||
}
|
||||
|
||||
tracks.set(index, entry);
|
||||
|
||||
if (entry.listener != null) entry.listener.start(index);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).start(index);
|
||||
}
|
||||
|
||||
/** @see #setAnimation(int, Animation, boolean) */
|
||||
@ -227,6 +239,7 @@ public class AnimationState {
|
||||
entry.loop = loop;
|
||||
entry.endTime = animation.getDuration();
|
||||
setCurrent(trackIndex, entry);
|
||||
queue.drain();
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -319,6 +332,7 @@ public class AnimationState {
|
||||
}
|
||||
|
||||
static public class TrackEntry implements Poolable {
|
||||
int index;
|
||||
TrackEntry next, previous;
|
||||
Animation animation;
|
||||
boolean loop;
|
||||
@ -422,37 +436,119 @@ public class AnimationState {
|
||||
return time >= endTime;
|
||||
}
|
||||
|
||||
public int getTrackIndex () {
|
||||
return index;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return animation == null ? "<none>" : animation.name;
|
||||
}
|
||||
}
|
||||
|
||||
class EventQueue {
|
||||
static private final int EVENT = 0, END = -1;
|
||||
|
||||
final Array objects = new Array();
|
||||
final IntArray loopCounts = new IntArray();
|
||||
|
||||
public void queueEvent (TrackEntry entry, Event event) {
|
||||
objects.add(entry);
|
||||
objects.add(event);
|
||||
loopCounts.add(EVENT);
|
||||
}
|
||||
|
||||
public void queueComplete (TrackEntry entry, int loopCount) {
|
||||
objects.add(entry);
|
||||
objects.add(null);
|
||||
loopCounts.add(loopCount);
|
||||
}
|
||||
|
||||
public void queueEnd (TrackEntry entry) {
|
||||
objects.add(entry);
|
||||
objects.add(null);
|
||||
loopCounts.add(END);
|
||||
}
|
||||
|
||||
public void drain () {
|
||||
Array objects = this.objects;
|
||||
IntArray loopCounts = this.loopCounts;
|
||||
Array<AnimationStateListener> listeners = AnimationState.this.listeners;
|
||||
int listenerCount = listeners.size;
|
||||
for (int i = 0, ii = 0, n = loopCounts.size; i < n; i++, ii += 2) {
|
||||
TrackEntry entry = (TrackEntry)objects.get(ii);
|
||||
int loopCount = loopCounts.get(i);
|
||||
switch (loopCount) {
|
||||
case EVENT:
|
||||
Event event = (Event)objects.get(ii + 1);
|
||||
if (entry.listener != null) entry.listener.event(entry, event);
|
||||
for (int iii = 0; iii < listenerCount; iii++)
|
||||
listeners.get(iii).event(entry, event);
|
||||
break;
|
||||
case END:
|
||||
fireEnd(entry);
|
||||
break;
|
||||
default:
|
||||
if (entry.listener != null) entry.listener.complete(entry, loopCount);
|
||||
for (int iii = 0; iii < listenerCount; iii++)
|
||||
listeners.get(iii).complete(entry, loopCount);
|
||||
}
|
||||
}
|
||||
objects.clear();
|
||||
loopCounts.clear();
|
||||
}
|
||||
|
||||
public void fireStart (TrackEntry entry) {
|
||||
if (entry.listener != null) entry.listener.start(entry);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).start(entry);
|
||||
}
|
||||
|
||||
public void fireEnd (TrackEntry entry) {
|
||||
if (entry.listener != null) entry.listener.end(entry);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).end(entry);
|
||||
trackEntryPool.free(entry);
|
||||
}
|
||||
|
||||
public void fireInterrupt (TrackEntry entry) {
|
||||
if (entry.listener != null) entry.listener.interrupt(entry);
|
||||
for (int i = 0, n = listeners.size; i < n; i++)
|
||||
listeners.get(i).interrupt(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static public interface AnimationStateListener {
|
||||
/** Invoked when the current animation triggers an event. */
|
||||
public void event (int trackIndex, Event event);
|
||||
/** Invoked when this animation triggers an event. */
|
||||
public void event (TrackEntry entry, Event event);
|
||||
|
||||
/** Invoked when the current animation has completed.
|
||||
/** Invoked every time this animation completes a loop.
|
||||
* @param loopCount The number of times the animation reached the end. */
|
||||
public void complete (int trackIndex, int loopCount);
|
||||
public void complete (TrackEntry entry, int loopCount);
|
||||
|
||||
/** Invoked just after the current animation is set. */
|
||||
public void start (int trackIndex);
|
||||
/** Invoked just after this animation is set. */
|
||||
public void start (TrackEntry entry);
|
||||
|
||||
/** Invoked just before the current animation is replaced. */
|
||||
public void end (int trackIndex);
|
||||
/** Invoked just after another animation is set. */
|
||||
public void interrupt (TrackEntry entry);
|
||||
|
||||
/** Invoked when this animation will no longer be applied. */
|
||||
public void end (TrackEntry entry);
|
||||
}
|
||||
|
||||
static public abstract class AnimationStateAdapter implements AnimationStateListener {
|
||||
public void event (int trackIndex, Event event) {
|
||||
public void event (TrackEntry entry, Event event) {
|
||||
}
|
||||
|
||||
public void complete (int trackIndex, int loopCount) {
|
||||
public void complete (TrackEntry entry, int loopCount) {
|
||||
}
|
||||
|
||||
public void start (int trackIndex) {
|
||||
public void start (TrackEntry entry) {
|
||||
}
|
||||
|
||||
public void end (int trackIndex) {
|
||||
public void interrupt (TrackEntry entry) {
|
||||
}
|
||||
|
||||
public void end (TrackEntry entry) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user