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));
}
public void event (TrackEntry entry, Event event) {
add(actual("event " + event.getString(), entry));
}
public void interrupt (TrackEntry 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) {
add(actual("complete", entry));
}
public void end (TrackEntry entry) {
add(actual("end", entry));
public void event (TrackEntry entry, Event event) {
add(actual("event " + event.getString(), entry));
}
private void add (Result result) {
@ -119,20 +123,36 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 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);
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, "event 0", 0, 0), //
expect(0, "event 14", 0.467f, 0.467f), //
expect(0, "event 30", 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.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);
setup("30 time step", // 3
@ -141,7 +161,8 @@ public class AnimationStateTest {
expect(0, "event 14", 30, 30), //
expect(0, "event 30", 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);
run(30, 1000, null);
@ -152,7 +173,8 @@ public class AnimationStateTest {
expect(0, "event 14", 1, 1), //
expect(0, "event 30", 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);
run(1, 1.01f, null);
@ -171,6 +193,7 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 1.1f), //
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 30", 1, 2), //
@ -183,11 +206,13 @@ public class AnimationStateTest {
expect(0, "event 0", 0.1f, 2.1f), //
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 30", 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.addAnimation(0, "events2", false, 0);
@ -206,11 +231,13 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 0.6f), //
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 30", 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.addAnimation(0, "events2", false, 0.5f);
@ -230,10 +257,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.4f), //
expect(0, "end", 1.6f, 1.7f), //
expect(0, "dispose", 1.6f, 1.7f), //
expect(1, "event 30", 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);
state.setAnimation(0, "events1", true);
@ -253,10 +282,12 @@ public class AnimationStateTest {
expect(0, "complete", 1, 1), //
expect(0, "end", 1.1f, 1.2f), //
expect(0, "dispose", 1.1f, 1.2f), //
expect(1, "event 30", 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);
state.setAnimation(0, "events1", false);
@ -277,10 +308,12 @@ public class AnimationStateTest {
expect(0, "complete", 1, 1), //
expect(0, "end", 1.1f, 1.2f), //
expect(0, "dispose", 1.1f, 1.2f), //
expect(1, "event 30", 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);
state.setAnimation(0, "events1", false).setEventThreshold(0.5f);
@ -305,10 +338,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.3f), //
expect(0, "end", 1.5f, 1.6f), //
expect(0, "dispose", 1.5f, 1.6f), //
expect(1, "event 30", 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.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(0, "end", 2.1f, 2.2f), //
expect(0, "dispose", 2.1f, 2.2f), //
expect(1, "event 14", 0.5f, 2.5f), //
expect(1, "event 30", 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.addAnimation(0, "events2", false, 2);
@ -380,11 +417,13 @@ public class AnimationStateTest {
expect(1, "event 0", 0.1f, 2.1f), //
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 30", 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);
run(0.1f, 6, new TestListener() {
@ -399,7 +438,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 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);
run(0.1f, 1.9f, null);
@ -410,7 +450,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 0.5f), //
expect(0, "event 30", 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);
run(0.1f, 10, null);
@ -447,7 +488,8 @@ public class AnimationStateTest {
expect(0, "start", 0, 0), //
expect(0, "event 14", 0.3f, 0.3f), //
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.setAnimationStart(0.2f);
@ -472,10 +514,12 @@ public class AnimationStateTest {
expect(1, "event 14", 0.5f, 1.2f), //
expect(0, "end", 1.4f, 1.5f), //
expect(0, "dispose", 1.4f, 1.5f), //
expect(1, "event 30", 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.setAnimationStart(0.2f);
@ -489,20 +533,24 @@ public class AnimationStateTest {
expect(0, "start", 0, 0), //
expect(0, "event 0", 0, 0), //
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, "complete", 1, 1), //
expect(1, "event 0", 0.1f, 1), //
expect(1, "event 14", 0.5f, 1.4f), //
expect(1, "event 0", 0.1f, 1.1f), //
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, "complete", 1, 1.9f), //
expect(1, "end", 1, 2) //
expect(1, "event 30", 1, 2), //
expect(1, "complete", 1, 2), //
expect(1, "end", 1, 2.1f), //
expect(1, "dispose", 1, 2.1f) //
);
state.setAnimation(0, "events1", true);
run(0.1f, 1000, new TestListener() {
@ -513,37 +561,39 @@ public class AnimationStateTest {
setup("setAnimation twice", // 21
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(0, "interrupt", 0, 0), //
expect(1, "event 0", 0, 0), //
expect(0, "end", 0.1f, 0.2f), //
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(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(0, "event 14", 0.5f, 1.2f), //
expect(0, "event 30", 1, 1.7f), //
expect(0, "complete", 1, 1.7f), //
expect(0, "end", 1, 1.8f) //
expect(1, "start", 0, 0.8f), //
expect(1, "event 0", 0.1f, 0.9f), //
expect(1, "event 14", 0.5f, 1.3f), //
expect(1, "event 30", 1, 1.8f), //
expect(1, "complete", 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, "events2", false);
run(0.1f, 1000, new TestListener() {
public void frame (float time) {
if (MathUtils.isEqual(time, 0.8f)) {
state.setAnimation(0, "events2", false); // First should be ignored.
state.setAnimation(0, "events1", false);
state.setAnimation(0, "events1", false); // First should be ignored.
state.setAnimation(0, "events2", false);
}
}
});
@ -554,7 +604,8 @@ public class AnimationStateTest {
expect(0, "event 14", 0.5f, 5.5f), //
expect(0, "event 30", 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);
run(0.1f, 10, null);
@ -569,23 +620,41 @@ public class AnimationStateTest {
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) {
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) {
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, "events2", false, 0);
state.setAnimation(1, "events2", false);
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.");
}
@ -608,7 +677,6 @@ public class AnimationStateTest {
state.apply(skeleton);
while (time < endTime) {
time += incr;
if (listener != null) listener.frame(time);
skeleton.update(incr);
state.update(incr);
@ -627,6 +695,8 @@ public class AnimationStateTest {
state.apply(skeleton);
state.apply(skeleton);
if (expected.size > 0) state.addListener(stateListener);
if (listener != null) listener.frame(time);
}
// Expecting more than actual is a failure.
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.setTimeScale(0.3f); // Slow all animations down to 30% speed.
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) {
System.out.println(entry.getTrackIndex() + " complete: " + entry);
public void start (TrackEntry entry) {
System.out.println(entry.getTrackIndex() + " start: " + entry);
}
public void interrupt (TrackEntry 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) {
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.

View File

@ -103,18 +103,18 @@ public class AnimationState {
// Should we get rid of the track end time?
// Or default it to MAX_VALUE even for non-looping animations?
// 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);
queue.end(current);
disposeNext(current);
if (mixingFrom != null) queue.end(mixingFrom);
continue;
}
current.trackTime += currentDelta;
if (mixingFrom != null) {
if (current.mixTime >= current.mixDuration && current.mixTime > 0) {
queue.end(mixingFrom);
current.mixingFrom = null;
queue.end(mixingFrom);
animationsChanged = true;
} else {
mixingFrom.animationLast = mixingFrom.nextAnimationLast;
@ -237,9 +237,12 @@ public class AnimationState {
}
public void clearTracks () {
for (int i = 0, n = tracks.size; i < n; i++)
clearTrack(i);
for (int i = 0, n = tracks.size; i < n; i++) {
TrackEntry current = tracks.get(i);
if (current != null) clearTrack(current);
}
tracks.clear();
queue.drain();
}
// 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;
TrackEntry current = tracks.get(trackIndex);
if (current == null) return;
freeAll(current.next);
queue.end(current);
if (current.mixingFrom != null) queue.end(current.mixingFrom);
clearTrack(current);
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. */
private void freeAll (TrackEntry entry) {
while (entry != null) {
TrackEntry next = entry.next;
trackEntryPool.free(entry);
entry = next;
private void disposeNext (TrackEntry entry) {
TrackEntry next = entry.next;
while (next != null) {
queue.dispose(next);
next = next.next;
}
entry.next = null;
}
private TrackEntry expandToIndex (int index) {
@ -285,7 +298,7 @@ public class AnimationState {
queue.interrupt(current);
// 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;
mixingFrom = current;
} else
@ -347,28 +360,26 @@ public class AnimationState {
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.
* 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.
/** Sets the current animation for a track, discarding any queued animations.
* @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)}. */
public TrackEntry setAnimation (int trackIndex, Animation animation, boolean loop) {
if (animation == null) throw new IllegalArgumentException("animation cannot be null.");
TrackEntry current = expandToIndex(trackIndex);
TrackEntry entry = trackEntry(trackIndex, animation, loop, current);
if (current == null) {
setCurrent(trackIndex, entry);
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;
}
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);
setCurrent(trackIndex, entry);
queue.drain();
return entry;
}
@ -696,11 +707,6 @@ public class AnimationState {
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. */
public boolean isComplete () {
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
* {@link AnimationStateData} based on the animation before this animation (if any).
* <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 () {
return mixDuration;
}
@ -740,7 +746,7 @@ public class AnimationState {
}
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 Pool<TrackEntry> trackEntryPool;
@ -779,6 +785,11 @@ public class AnimationState {
eventTypes.add(END);
}
public void dispose (TrackEntry entry) {
objects.add(entry);
eventTypes.add(DISPOSE);
}
public void drain () {
if (draining) return; // Not reentrant.
draining = true;
@ -810,6 +821,11 @@ public class AnimationState {
if (entry.listener != null) entry.listener.end(entry);
for (int i = 0; i < listeners.size; i++)
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);
break;
default:
@ -830,31 +846,28 @@ public class AnimationState {
}
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);
/** Invoked just after another animation is set as the current animation. The animation may continue being applied if there
* is a mix duration. */
/** Invoked just after another entry is set to replace this entry as the current entry. This entry may continue being
* applied for mixing. */
public void interrupt (TrackEntry entry);
/** Invoked when this animation will no longer be applied. After this method returns, no references to the track entry
* should be kept because it may be reused. */
/** Invoked just before this entry will no longer be the current entry and will never be applied again. */
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);
/** Invoked when this animation triggers an event. */
/** Invoked when this entry's animation triggers an event. */
public void event (TrackEntry entry, Event event);
}
static public abstract class AnimationStateAdapter implements AnimationStateListener {
public void event (TrackEntry entry, Event event) {
}
public void complete (TrackEntry entry) {
}
public void start (TrackEntry entry) {
}
@ -863,5 +876,14 @@ public class AnimationState {
public void end (TrackEntry entry) {
}
public void dispose (TrackEntry entry) {
}
public void event (TrackEntry entry, Event event) {
}
public void complete (TrackEntry entry) {
}
}
}