Searching for sanity.

This commit is contained in:
NathanSweet 2016-08-23 16:39:16 +02:00
parent 0a90565ba1
commit 3c57c03952
3 changed files with 213 additions and 116 deletions

View File

@ -67,20 +67,24 @@ public class AnimationStateTest {
add(actual("start", entry)); add(actual("start", entry));
} }
public void event (TrackEntry entry, Event event) {
add(actual("event " + event.getString(), entry));
}
public void interrupt (TrackEntry entry) { public void interrupt (TrackEntry entry) {
add(actual("interrupt", entry)); add(actual("interrupt", entry));
} }
public void end (TrackEntry entry) {
add(actual("end", entry));
}
public void dispose (TrackEntry entry) {
add(actual("dispose", entry));
}
public void complete (TrackEntry entry) { public void complete (TrackEntry entry) {
add(actual("complete", entry)); add(actual("complete", entry));
} }
public void end (TrackEntry entry) { public void event (TrackEntry entry, Event event) {
add(actual("end", entry)); add(actual("event " + event.getString(), entry));
} }
private void add (Result result) { private void add (Result result) {
@ -119,20 +123,36 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), // expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 1, 1), // expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1, 1.1f) // expect(0, "end", 1, 1.1f), //
expect(0, "dispose", 1, 1.1f) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
run(0.1f, 1000, null); run(0.1f, 1000, null);
setup("1/60 time step", // 2 setup("1/60 time step, dispose queued", // 2
expect(0, "start", 0, 0), //
expect(0, "interrupt", 0, 0), //
expect(0, "end", 0, 0), //
expect(0, "dispose", 0, 0), //
expect(1, "dispose", 0, 0), //
expect(0, "dispose", 0, 0), //
expect(1, "dispose", 0, 0), //
// First 2 set/addAnimation calls are done.
expect(0, "start", 0, 0), // expect(0, "start", 0, 0), //
expect(0, "event 0", 0, 0), // expect(0, "event 0", 0, 0), //
expect(0, "event 14", 0.467f, 0.467f), // expect(0, "event 14", 0.467f, 0.467f), //
expect(0, "event 30", 1, 1), // expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1, 1.017f) // expect(0, "end", 1, 1.017f), //
expect(0, "dispose", 1, 1.017f) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
state.addAnimation(0, "events2", false, 0);
state.addAnimation(0, "events1", false, 0);
state.addAnimation(0, "events2", false, 0);
state.setAnimation(0, "events1", false);
run(1 / 60f, 1000, null); run(1 / 60f, 1000, null);
setup("30 time step", // 3 setup("30 time step", // 3
@ -141,7 +161,8 @@ public class AnimationStateTest {
expect(0, "event 14", 30, 30), // expect(0, "event 14", 30, 30), //
expect(0, "event 30", 30, 30), // expect(0, "event 30", 30, 30), //
expect(0, "complete", 30, 30), // expect(0, "complete", 30, 30), //
expect(0, "end", 30, 60) // expect(0, "end", 30, 60), //
expect(0, "dispose", 30, 60) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
run(30, 1000, null); run(30, 1000, null);
@ -152,7 +173,8 @@ public class AnimationStateTest {
expect(0, "event 14", 1, 1), // expect(0, "event 14", 1, 1), //
expect(0, "event 30", 1, 1), // expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1, 2) // expect(0, "end", 1, 2), //
expect(0, "dispose", 1, 2) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
run(1, 1.01f, null); run(1, 1.01f, null);
@ -171,6 +193,7 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 1.1f), // expect(1, "event 0", 0.1f, 1.1f), //
expect(0, "end", 1.1f, 1.2f), // expect(0, "end", 1.1f, 1.2f), //
expect(0, "dispose", 1.1f, 1.2f), //
expect(1, "event 14", 0.5f, 1.5f), // expect(1, "event 14", 0.5f, 1.5f), //
expect(1, "event 30", 1, 2), // expect(1, "event 30", 1, 2), //
@ -183,11 +206,13 @@ public class AnimationStateTest {
expect(0, "event 0", 0.1f, 2.1f), // expect(0, "event 0", 0.1f, 2.1f), //
expect(1, "end", 1.1f, 2.2f), // expect(1, "end", 1.1f, 2.2f), //
expect(1, "dispose", 1.1f, 2.2f), //
expect(0, "event 14", 0.5f, 2.5f), // expect(0, "event 14", 0.5f, 2.5f), //
expect(0, "event 30", 1, 3), // expect(0, "event 30", 1, 3), //
expect(0, "complete", 1, 3), // expect(0, "complete", 1, 3), //
expect(0, "end", 1, 3.1f) // expect(0, "end", 1, 3.1f), //
expect(0, "dispose", 1, 3.1f) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
state.addAnimation(0, "events2", false, 0); state.addAnimation(0, "events2", false, 0);
@ -206,11 +231,13 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 0.6f), // expect(1, "event 0", 0.1f, 0.6f), //
expect(0, "end", 0.6f, 0.7f), // expect(0, "end", 0.6f, 0.7f), //
expect(0, "dispose", 0.6f, 0.7f), //
expect(1, "event 14", 0.5f, 1.0f), // expect(1, "event 14", 0.5f, 1.0f), //
expect(1, "event 30", 1, 1.5f), // expect(1, "event 30", 1, 1.5f), //
expect(1, "complete", 1, 1.5f), // expect(1, "complete", 1, 1.5f), //
expect(1, "end", 1, 1.6f) // expect(1, "end", 1, 1.6f), //
expect(1, "dispose", 1, 1.6f) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
state.addAnimation(0, "events2", false, 0.5f); state.addAnimation(0, "events2", false, 0.5f);
@ -230,10 +257,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.4f), // expect(1, "event 14", 0.5f, 1.4f), //
expect(0, "end", 1.6f, 1.7f), // expect(0, "end", 1.6f, 1.7f), //
expect(0, "dispose", 1.6f, 1.7f), //
expect(1, "event 30", 1, 1.9f), // expect(1, "event 30", 1, 1.9f), //
expect(1, "complete", 1, 1.9f), // expect(1, "complete", 1, 1.9f), //
expect(1, "end", 1, 2) // expect(1, "end", 1, 2), //
expect(1, "dispose", 1, 2) //
); );
stateData.setMix("events1", "events2", 0.7f); stateData.setMix("events1", "events2", 0.7f);
state.setAnimation(0, "events1", true); state.setAnimation(0, "events1", true);
@ -253,10 +282,12 @@ public class AnimationStateTest {
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1.1f, 1.2f), // expect(0, "end", 1.1f, 1.2f), //
expect(0, "dispose", 1.1f, 1.2f), //
expect(1, "event 30", 1, 1.4f), // expect(1, "event 30", 1, 1.4f), //
expect(1, "complete", 1, 1.4f), // expect(1, "complete", 1, 1.4f), //
expect(1, "end", 1, 1.5f) // expect(1, "end", 1, 1.5f), //
expect(1, "dispose", 1, 1.5f) //
); );
stateData.setDefaultMix(0.7f); stateData.setDefaultMix(0.7f);
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
@ -277,10 +308,12 @@ public class AnimationStateTest {
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1.1f, 1.2f), // expect(0, "end", 1.1f, 1.2f), //
expect(0, "dispose", 1.1f, 1.2f), //
expect(1, "event 30", 1, 1.4f), // expect(1, "event 30", 1, 1.4f), //
expect(1, "complete", 1, 1.4f), // expect(1, "complete", 1, 1.4f), //
expect(1, "end", 1, 1.5f) // expect(1, "end", 1, 1.5f), //
expect(1, "dispose", 1, 1.5f) //
); );
stateData.setMix("events1", "events2", 0.7f); stateData.setMix("events1", "events2", 0.7f);
state.setAnimation(0, "events1", false).setEventThreshold(0.5f); state.setAnimation(0, "events1", false).setEventThreshold(0.5f);
@ -305,10 +338,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.3f), // expect(1, "event 14", 0.5f, 1.3f), //
expect(0, "end", 1.5f, 1.6f), // expect(0, "end", 1.5f, 1.6f), //
expect(0, "dispose", 1.5f, 1.6f), //
expect(1, "event 30", 1, 1.8f), // expect(1, "event 30", 1, 1.8f), //
expect(1, "complete", 1, 1.8f), // expect(1, "complete", 1, 1.8f), //
expect(1, "end", 1, 1.9f) // expect(1, "end", 1, 1.9f), //
expect(1, "dispose", 1, 1.9f) //
); );
state.setAnimation(0, "events1", true).setEventThreshold(1); state.setAnimation(0, "events1", true).setEventThreshold(1);
state.addAnimation(0, "events2", false, 0.8f).setMixDuration(0.7f); state.addAnimation(0, "events2", false, 0.8f).setMixDuration(0.7f);
@ -351,11 +386,13 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 2.1f), // expect(1, "event 0", 0.1f, 2.1f), //
expect(0, "end", 2.1f, 2.2f), // expect(0, "end", 2.1f, 2.2f), //
expect(0, "dispose", 2.1f, 2.2f), //
expect(1, "event 14", 0.5f, 2.5f), // expect(1, "event 14", 0.5f, 2.5f), //
expect(1, "event 30", 1, 3), // expect(1, "event 30", 1, 3), //
expect(1, "complete", 1, 3), // expect(1, "complete", 1, 3), //
expect(1, "end", 1, 3.1f) // expect(1, "end", 1, 3.1f), //
expect(1, "dispose", 1, 3.1f) //
); );
state.setAnimation(0, "events1", false); state.setAnimation(0, "events1", false);
state.addAnimation(0, "events2", false, 2); state.addAnimation(0, "events2", false, 2);
@ -380,11 +417,13 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 2.1f), // expect(1, "event 0", 0.1f, 2.1f), //
expect(0, "end", 2.1f, 2.2f), // expect(0, "end", 2.1f, 2.2f), //
expect(0, "dispose", 2.1f, 2.2f), //
expect(1, "event 14", 0.5f, 2.5f), // expect(1, "event 14", 0.5f, 2.5f), //
expect(1, "event 30", 1, 3), // expect(1, "event 30", 1, 3), //
expect(1, "complete", 1, 3), // expect(1, "complete", 1, 3), //
expect(1, "end", 1, 3.1f) // expect(1, "end", 1, 3.1f), //
expect(1, "dispose", 1, 3.1f) //
); );
state.setAnimation(0, "events1", true); state.setAnimation(0, "events1", true);
run(0.1f, 6, new TestListener() { run(0.1f, 6, new TestListener() {
@ -399,7 +438,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), // expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 1, 1), // expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 1, 1.1f) // expect(0, "end", 1, 1.1f), //
expect(0, "dispose", 1, 1.1f) //
); );
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
run(0.1f, 1.9f, null); run(0.1f, 1.9f, null);
@ -410,7 +450,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), // expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 1, 1), // expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), // expect(0, "complete", 1, 1), //
expect(0, "end", 9f, 9.1f) // expect(0, "end", 9f, 9.1f), //
expect(0, "dispose", 9f, 9.1f) //
); );
state.setAnimation(0, "events1", false).setTrackEnd(9); state.setAnimation(0, "events1", false).setTrackEnd(9);
run(0.1f, 10, null); run(0.1f, 10, null);
@ -447,7 +488,8 @@ public class AnimationStateTest {
expect(0, "start", 0, 0), // expect(0, "start", 0, 0), //
expect(0, "event 14", 0.3f, 0.3f), // expect(0, "event 14", 0.3f, 0.3f), //
expect(0, "complete", 0.6f, 0.6f), // expect(0, "complete", 0.6f, 0.6f), //
expect(0, "end", 1, 1.1f) // expect(0, "end", 1, 1.1f), //
expect(0, "dispose", 1, 1.1f) //
); );
entry = state.setAnimation(0, "events1", false); entry = state.setAnimation(0, "events1", false);
entry.setAnimationStart(0.2f); entry.setAnimationStart(0.2f);
@ -472,10 +514,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.2f), // expect(1, "event 14", 0.5f, 1.2f), //
expect(0, "end", 1.4f, 1.5f), // expect(0, "end", 1.4f, 1.5f), //
expect(0, "dispose", 1.4f, 1.5f), //
expect(1, "event 30", 1, 1.7f), // expect(1, "event 30", 1, 1.7f), //
expect(1, "complete", 1, 1.7f), // expect(1, "complete", 1, 1.7f), //
expect(1, "end", 1, 1.8f) // expect(1, "end", 1, 1.8f), //
expect(1, "dispose", 1, 1.8f) //
); );
entry = state.setAnimation(0, "events1", true); entry = state.setAnimation(0, "events1", true);
entry.setAnimationStart(0.2f); entry.setAnimationStart(0.2f);
@ -489,20 +533,24 @@ public class AnimationStateTest {
expect(0, "start", 0, 0), // expect(0, "start", 0, 0), //
expect(0, "event 0", 0, 0), // expect(0, "event 0", 0, 0), //
expect(0, "event 14", 0.5f, 0.5f), // expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 1, 1), //
expect(0, "complete", 1, 1), //
expect(0, "event 0", 1, 1), //
expect(1, "start", 0.1f, 1), // expect(1, "start", 0, 1), //
expect(0, "interrupt", 1, 1), // expect(0, "interrupt", 1, 1), //
expect(0, "complete", 1, 1), //
expect(1, "event 0", 0.1f, 1), // expect(1, "event 0", 0.1f, 1.1f), //
expect(1, "event 14", 0.5f, 1.4f), // expect(1, "event 14", 0.5f, 1.5f), //
expect(0, "end", 1.6f, 1.7f), // expect(0, "end", 1.7f, 1.8f), //
expect(0, "dispose", 1.7f, 1.8f), //
expect(1, "event 30", 1, 1.9f), // expect(1, "event 30", 1, 2), //
expect(1, "complete", 1, 1.9f), // expect(1, "complete", 1, 2), //
expect(1, "end", 1, 2) // expect(1, "end", 1, 2.1f), //
expect(1, "dispose", 1, 2.1f) //
); );
state.setAnimation(0, "events1", true); state.setAnimation(0, "events1", true);
run(0.1f, 1000, new TestListener() { run(0.1f, 1000, new TestListener() {
@ -513,37 +561,39 @@ public class AnimationStateTest {
setup("setAnimation twice", // 21 setup("setAnimation twice", // 21
expect(0, "start", 0, 0), // expect(0, "start", 0, 0), //
expect(0, "interrupt", 0, 0), //
expect(0, "end", 0, 0), //
expect(0, "dispose", 0, 0), //
expect(1, "start", 0, 0), // expect(1, "start", 0, 0), //
expect(0, "interrupt", 0, 0), //
expect(1, "event 0", 0, 0), // expect(1, "event 0", 0, 0), //
expect(0, "end", 0.1f, 0.2f), //
expect(1, "event 14", 0.5f, 0.5f), // expect(1, "event 14", 0.5f, 0.5f), //
expect(0, "start", 0.1f, 0.8f), // // First 2 setAnimation calls are done.
expect(0, "start", 0, 0.8f), //
expect(1, "interrupt", 0.8f, 0.8f), // expect(1, "interrupt", 0.8f, 0.8f), //
expect(0, "event 0", 0.1f, 0.8f), // expect(0, "interrupt", 0, 0.8f), //
expect(0, "end", 0, 0.8f), //
expect(0, "dispose", 0, 0.8f), //
expect(1, "end", 0.8f, 0.9f), // expect(1, "start", 0, 0.8f), //
expect(1, "event 0", 0.1f, 0.9f), //
expect(0, "event 14", 0.5f, 1.2f), // expect(1, "event 14", 0.5f, 1.3f), //
expect(0, "event 30", 1, 1.7f), // expect(1, "event 30", 1, 1.8f), //
expect(0, "complete", 1, 1.7f), // expect(1, "complete", 1, 1.8f), //
expect(0, "end", 1, 1.8f) // expect(1, "end", 1, 1.9f), //
expect(1, "dispose", 1, 1.9f) //
); );
state.setAnimation(0, "events1", false); // First should be ignored. state.setAnimation(0, "events1", false); // First should be ignored.
state.setAnimation(0, "events2", false); state.setAnimation(0, "events2", false);
run(0.1f, 1000, new TestListener() { run(0.1f, 1000, new TestListener() {
public void frame (float time) { public void frame (float time) {
if (MathUtils.isEqual(time, 0.8f)) { if (MathUtils.isEqual(time, 0.8f)) {
state.setAnimation(0, "events2", false); // First should be ignored. state.setAnimation(0, "events1", false); // First should be ignored.
state.setAnimation(0, "events1", false); state.setAnimation(0, "events2", false);
} }
} }
}); });
@ -554,7 +604,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 5.5f), // expect(0, "event 14", 0.5f, 5.5f), //
expect(0, "event 30", 1, 6), // expect(0, "event 30", 1, 6), //
expect(0, "complete", 1, 6), // expect(0, "complete", 1, 6), //
expect(0, "end", 1, 6.1f) // expect(0, "end", 1, 6.1f), //
expect(0, "dispose", 1, 6.1f) //
); );
state.addAnimation(0, "events1", false, 5); state.addAnimation(0, "events1", false, 5);
run(0.1f, 10, null); run(0.1f, 10, null);
@ -569,23 +620,41 @@ public class AnimationStateTest {
state.addAnimation(3, "events2", false, 0); state.addAnimation(3, "events2", false, 0);
} }
public void event (TrackEntry entry, Event event) {
if (entry.getTrackIndex() != 2) state.setAnimation(2, "events2", false);
}
public void end (TrackEntry entry) { public void end (TrackEntry entry) {
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(0, "events2", false); if (entry.getAnimation().getName().equals("events1")) state.setAnimation(0, "events2", false);
} }
public void dispose (TrackEntry entry) {
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(1, "events2", false);
}
public void complete (TrackEntry entry) { public void complete (TrackEntry entry) {
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(1, "events2", false); if (entry.getAnimation().getName().equals("events1")) state.setAnimation(1, "events2", false);
} }
public void event (TrackEntry entry, Event event) {
if (entry.getTrackIndex() != 2) state.setAnimation(2, "events2", false);
}
}); });
state.addAnimation(0, "events1", false, 0); state.addAnimation(0, "events1", false, 0);
state.addAnimation(0, "events2", false, 0); state.addAnimation(0, "events2", false, 0);
state.setAnimation(1, "events2", false); state.setAnimation(1, "events2", false);
run(0.1f, 10, null); run(0.1f, 10, null);
setup("clearTrack", // 24
expect(0, "start", 0, 0), //
expect(0, "event 0", 0, 0), //
expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "end", 0.7f, 0.7f), //
expect(0, "dispose", 0.7f, 0.7f) //
);
state.addAnimation(0, "events1", false, 0);
run(0.1f, 10, new TestListener() {
public void frame (float time) {
if (MathUtils.isEqual(time, 0.7f)) state.clearTrack(0);
}
});
System.out.println("AnimationState tests passed."); System.out.println("AnimationState tests passed.");
} }
@ -608,7 +677,6 @@ public class AnimationStateTest {
state.apply(skeleton); state.apply(skeleton);
while (time < endTime) { while (time < endTime) {
time += incr; time += incr;
if (listener != null) listener.frame(time);
skeleton.update(incr); skeleton.update(incr);
state.update(incr); state.update(incr);
@ -627,6 +695,8 @@ public class AnimationStateTest {
state.apply(skeleton); state.apply(skeleton);
state.apply(skeleton); state.apply(skeleton);
if (expected.size > 0) state.addListener(stateListener); if (expected.size > 0) state.addListener(stateListener);
if (listener != null) listener.frame(time);
} }
// Expecting more than actual is a failure. // Expecting more than actual is a failure.
for (int i = actual.size, n = expected.size; i < n; i++) { for (int i = actual.size, n = expected.size; i < n; i++) {

View File

@ -83,26 +83,31 @@ public class SimpleTest2 extends ApplicationAdapter {
state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc). 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.setTimeScale(0.3f); // Slow all animations down to 30% speed.
state.addListener(new AnimationStateListener() { state.addListener(new AnimationStateListener() {
public void event (TrackEntry entry, Event event) {
System.out
.println(entry.getTrackIndex() + " event: " + entry + ", " + event.getData().getName() + ", " + event.getInt());
}
public void complete (TrackEntry entry) { public void start (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " complete: " + entry); System.out.println(entry.getTrackIndex() + " start: " + entry);
} }
public void interrupt (TrackEntry entry) { public void interrupt (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " interrupt: " + entry); System.out.println(entry.getTrackIndex() + " interrupt: " + entry);
} }
public void start (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " start: " + entry);
}
public void end (TrackEntry entry) { public void end (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " end: " + entry); System.out.println(entry.getTrackIndex() + " end: " + entry);
} }
public void dispose (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " dispose: " + entry);
}
public void complete (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " complete: " + entry);
}
public void event (TrackEntry entry, Event event) {
System.out
.println(entry.getTrackIndex() + " event: " + entry + ", " + event.getData().getName() + ", " + event.getInt());
}
}); });
// Set animation on track 0. // Set animation on track 0.

View File

@ -103,18 +103,18 @@ public class AnimationState {
// Should we get rid of the track end time? // Should we get rid of the track end time?
// Or default it to MAX_VALUE even for non-looping animations? // Or default it to MAX_VALUE even for non-looping animations?
// Or reset the skeleton before clearing? Note only apply() has a skeleton. // Or reset the skeleton before clearing? Note only apply() has a skeleton.
freeAll(current.next);
queue.end(current);
if (mixingFrom != null) queue.end(mixingFrom);
tracks.set(i, null); tracks.set(i, null);
queue.end(current);
disposeNext(current);
if (mixingFrom != null) queue.end(mixingFrom);
continue; continue;
} }
current.trackTime += currentDelta; current.trackTime += currentDelta;
if (mixingFrom != null) { if (mixingFrom != null) {
if (current.mixTime >= current.mixDuration && current.mixTime > 0) { if (current.mixTime >= current.mixDuration && current.mixTime > 0) {
queue.end(mixingFrom);
current.mixingFrom = null; current.mixingFrom = null;
queue.end(mixingFrom);
animationsChanged = true; animationsChanged = true;
} else { } else {
mixingFrom.animationLast = mixingFrom.nextAnimationLast; mixingFrom.animationLast = mixingFrom.nextAnimationLast;
@ -237,9 +237,12 @@ public class AnimationState {
} }
public void clearTracks () { public void clearTracks () {
for (int i = 0, n = tracks.size; i < n; i++) for (int i = 0, n = tracks.size; i < n; i++) {
clearTrack(i); TrackEntry current = tracks.get(i);
if (current != null) clearTrack(current);
}
tracks.clear(); tracks.clear();
queue.drain();
} }
// BOZO - This leaves the skeleton in the last pose, with no easy way of resetting. // BOZO - This leaves the skeleton in the last pose, with no easy way of resetting.
@ -247,22 +250,32 @@ public class AnimationState {
if (trackIndex >= tracks.size) return; if (trackIndex >= tracks.size) return;
TrackEntry current = tracks.get(trackIndex); TrackEntry current = tracks.get(trackIndex);
if (current == null) return; if (current == null) return;
freeAll(current.next); clearTrack(current);
queue.end(current);
if (current.mixingFrom != null) queue.end(current.mixingFrom);
queue.drain(); queue.drain();
}
tracks.set(trackIndex, null); private void clearTrack (TrackEntry current) {
queue.end(current);
disposeNext(current);
TrackEntry mixingFrom = current.mixingFrom;
if (mixingFrom != null) {
current.mixingFrom = null;
queue.end(mixingFrom);
}
tracks.set(current.trackIndex, null);
} }
/** @param entry May be null. */ /** @param entry May be null. */
private void freeAll (TrackEntry entry) { private void disposeNext (TrackEntry entry) {
while (entry != null) {
TrackEntry next = entry.next; TrackEntry next = entry.next;
trackEntryPool.free(entry); while (next != null) {
entry = next; queue.dispose(next);
next = next.next;
} }
entry.next = null;
} }
private TrackEntry expandToIndex (int index) { private TrackEntry expandToIndex (int index) {
@ -285,7 +298,7 @@ public class AnimationState {
queue.interrupt(current); queue.interrupt(current);
// If a mix is in progress, mix from the closest animation. // If a mix is in progress, mix from the closest animation.
if (mixingFrom != null && current.mixTime / current.mixDuration < 0.5f) { if (mixingFrom != null && (current.mixDuration == 0 || current.mixTime / current.mixDuration < 0.5f)) {
entry.mixingFrom = mixingFrom; entry.mixingFrom = mixingFrom;
mixingFrom = current; mixingFrom = current;
} else } else
@ -347,28 +360,26 @@ public class AnimationState {
return setAnimation(trackIndex, animation, loop); return setAnimation(trackIndex, animation, loop);
} }
/** Sets the current animation for a track. If the track is empty, the new animation is made the current animation immediately. /** Sets the current animation for a track, discarding any queued animations.
* Otherwise, any queued animations are discarded and the new animation is queued to become the current animation the next time
* {@link #update(float)} is called.
* @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
* after {@link AnimationStateListener#end(TrackEntry)}. */ * after {@link AnimationStateListener#end(TrackEntry)}. */
public TrackEntry setAnimation (int trackIndex, Animation animation, boolean loop) { public TrackEntry setAnimation (int trackIndex, Animation animation, boolean loop) {
if (animation == null) throw new IllegalArgumentException("animation cannot be null."); if (animation == null) throw new IllegalArgumentException("animation cannot be null.");
TrackEntry current = expandToIndex(trackIndex); TrackEntry current = expandToIndex(trackIndex);
if (current != null) {
if (current.nextTrackLast == -1) {
// Don't mix from an entry that was never applied.
tracks.set(trackIndex, null);
queue.interrupt(current);
queue.end(current);
disposeNext(current);
current = null;
} else
disposeNext(current);
}
TrackEntry entry = trackEntry(trackIndex, animation, loop, current); TrackEntry entry = trackEntry(trackIndex, animation, loop, current);
if (current == null) {
setCurrent(trackIndex, entry); setCurrent(trackIndex, entry);
queue.drain(); queue.drain();
} else {
freeAll(current.next);
if (current.nextTrackLast == -1) { // If current was never applied, replace it.
setCurrent(trackIndex, entry);
queue.drain();
} else {
current.next = entry;
entry.delay = current.nextTrackLast;
}
}
return entry; return entry;
} }
@ -696,11 +707,6 @@ public class AnimationState {
return next; return next;
} }
/** @param next May be null. */
public void setNext (TrackEntry next) {
this.next = next;
}
/** Returns true if at least one loop has been completed. */ /** Returns true if at least one loop has been completed. */
public boolean isComplete () { public boolean isComplete () {
return trackTime >= animationEnd - animationStart; return trackTime >= animationEnd - animationStart;
@ -719,7 +725,7 @@ public class AnimationState {
/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by /** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
* {@link AnimationStateData} based on the animation before this animation (if any). * {@link AnimationStateData} based on the animation before this animation (if any).
* <p> * <p>
* The mix duration must be set before this track entry becomes the current track entry. */ * The mix duration must be set before the next time the animation state is updated. */
public float getMixDuration () { public float getMixDuration () {
return mixDuration; return mixDuration;
} }
@ -740,7 +746,7 @@ public class AnimationState {
} }
static private class EventQueue { static private class EventQueue {
static private final int START = 0, EVENT = 1, COMPLETE = 2, INTERRUPT = 3, END = 4; static private final int START = 0, EVENT = 1, COMPLETE = 2, INTERRUPT = 3, END = 4, DISPOSE = 5;
private final Array<AnimationStateListener> listeners; private final Array<AnimationStateListener> listeners;
private final Pool<TrackEntry> trackEntryPool; private final Pool<TrackEntry> trackEntryPool;
@ -779,6 +785,11 @@ public class AnimationState {
eventTypes.add(END); eventTypes.add(END);
} }
public void dispose (TrackEntry entry) {
objects.add(entry);
eventTypes.add(DISPOSE);
}
public void drain () { public void drain () {
if (draining) return; // Not reentrant. if (draining) return; // Not reentrant.
draining = true; draining = true;
@ -810,6 +821,11 @@ public class AnimationState {
if (entry.listener != null) entry.listener.end(entry); if (entry.listener != null) entry.listener.end(entry);
for (int i = 0; i < listeners.size; i++) for (int i = 0; i < listeners.size; i++)
listeners.get(i).end(entry); listeners.get(i).end(entry);
// Fall through.
case DISPOSE:
if (entry.listener != null) entry.listener.end(entry);
for (int i = 0; i < listeners.size; i++)
listeners.get(i).dispose(entry);
trackEntryPool.free(entry); trackEntryPool.free(entry);
break; break;
default: default:
@ -830,31 +846,28 @@ public class AnimationState {
} }
static public interface AnimationStateListener { static public interface AnimationStateListener {
/** Invoked just after this animation is set as the current animation. */ /** Invoked just after this entry is set as the current entry. */
public void start (TrackEntry entry); public void start (TrackEntry entry);
/** Invoked just after another animation is set as the current animation. The animation may continue being applied if there /** Invoked just after another entry is set to replace this entry as the current entry. This entry may continue being
* is a mix duration. */ * applied for mixing. */
public void interrupt (TrackEntry entry); public void interrupt (TrackEntry entry);
/** Invoked when this animation will no longer be applied. After this method returns, no references to the track entry /** Invoked just before this entry will no longer be the current entry and will never be applied again. */
* should be kept because it may be reused. */
public void end (TrackEntry entry); public void end (TrackEntry entry);
/** Invoked every time this animation completes a loop. */ /** Invoked just before this track entry will be disposed. References to the entry should not be kept after dispose is
* called, as it may be destroyed or reused. */
public void dispose (TrackEntry entry);
/** Invoked every time this entry's animation completes a loop. */
public void complete (TrackEntry entry); public void complete (TrackEntry entry);
/** Invoked when this animation triggers an event. */ /** Invoked when this entry's animation triggers an event. */
public void event (TrackEntry entry, Event event); public void event (TrackEntry entry, Event event);
} }
static public abstract class AnimationStateAdapter implements AnimationStateListener { static public abstract class AnimationStateAdapter implements AnimationStateListener {
public void event (TrackEntry entry, Event event) {
}
public void complete (TrackEntry entry) {
}
public void start (TrackEntry entry) { public void start (TrackEntry entry) {
} }
@ -863,5 +876,14 @@ public class AnimationState {
public void end (TrackEntry entry) { public void end (TrackEntry entry) {
} }
public void dispose (TrackEntry entry) {
}
public void event (TrackEntry entry, Event event) {
}
public void complete (TrackEntry entry) {
}
} }
} }