Fixes for AnimationState event order.

This commit is contained in:
NathanSweet 2016-06-26 15:27:46 +02:00
parent 534ffc8d1d
commit 9171f79cc2
5 changed files with 403 additions and 174 deletions

View File

@ -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" },

View File

@ -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 {

View File

@ -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);

View File

@ -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) {
}
}
}