mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +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": [
|
"bones": [
|
||||||
{ "name": "root" }
|
{ "name": "root" }
|
||||||
],
|
],
|
||||||
@ -7,7 +7,14 @@
|
|||||||
"event": {}
|
"event": {}
|
||||||
},
|
},
|
||||||
"animations": {
|
"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": [
|
"events": [
|
||||||
{ "time": 0, "name": "event", "string": "0" },
|
{ "time": 0, "name": "event", "string": "0" },
|
||||||
{ "time": 0.4666, "name": "event", "string": "14" },
|
{ "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.backends.lwjgl.LwjglFileHandle;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
||||||
|
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
|
||||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||||
|
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||||
|
|
||||||
public class AnimationStateTest {
|
public class AnimationStateTest {
|
||||||
final SkeletonJson json = new SkeletonJson(new AttachmentLoader() {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegionAttachment newRegionAttachment (Skin skin, String name, String path) {
|
public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,154 +61,274 @@ public class AnimationStateTest {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AnimationStateListener stateListener = new AnimationStateListener() {
|
final AnimationStateListener stateListener = new AnimationStateListener() {
|
||||||
public void start (int trackIndex) {
|
public void start (TrackEntry entry) {
|
||||||
actual.add(new Result("start", null));
|
add(actual("start", entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void event (int trackIndex, Event event) {
|
public void event (TrackEntry entry, Event event) {
|
||||||
actual.add(new Result("event", event.getString()));
|
add(actual("event " + event.getString(), entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void complete (int trackIndex, int loopCount) {
|
public void interrupt (TrackEntry entry) {
|
||||||
actual.add(new Result("complete", null));
|
add(actual("interrupt", entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void end (int trackIndex) {
|
public void complete (TrackEntry entry, int loopCount) {
|
||||||
actual.add(new Result("end", null));
|
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 SkeletonData skeletonData;
|
||||||
final AnimationStateData stateData;
|
final AnimationStateData stateData;
|
||||||
final Array<Result> actual = new Array();
|
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));
|
skeletonData = json.readSkeletonData(new LwjglFileHandle("test/test.json", FileType.Internal));
|
||||||
stateData = new AnimationStateData(skeletonData);
|
stateData = new AnimationStateData(skeletonData);
|
||||||
|
|
||||||
AnimationState state;
|
setup( // 1
|
||||||
|
expect("start", 0, 0, 0), //
|
||||||
state = newState();
|
expect("event 0", 0, 0, 0), //
|
||||||
state.setAnimation(0, "events", false);
|
expect("event 14", 0, 0.5f, 0.5f), //
|
||||||
test(state, 1 / 60f, 1000, //
|
expect("event 30", 0, 1, 1), //
|
||||||
new Result("start", null), //
|
expect("complete 1", 0, 1, 1), //
|
||||||
new Result("event", "0"), //
|
expect("end", 0, 1, 1.1f) //
|
||||||
new Result("event", "14"), //
|
|
||||||
new Result("event", "30"), //
|
|
||||||
new Result("complete", null), //
|
|
||||||
new Result("end", null) //
|
|
||||||
);
|
);
|
||||||
|
state.setAnimation(0, "events1", false);
|
||||||
|
run(0.1f, 1000);
|
||||||
|
|
||||||
state = newState();
|
setup( // 2
|
||||||
state.setAnimation(0, "events", false);
|
expect("start", 0, 0, 0), //
|
||||||
test(state, 30, 1000, //
|
expect("event 0", 0, 0, 0), //
|
||||||
new Result("start", null), //
|
expect("event 14", 0, 0.467f, 0.467f), //
|
||||||
new Result("event", "0"), //
|
expect("event 30", 0, 1.017f, 1.017f), //
|
||||||
new Result("event", "14"), //
|
expect("complete 1", 0, 1.017f, 1.017f), //
|
||||||
new Result("event", "30"), //
|
expect("end", 0, 1.017f, 1.033f) //
|
||||||
new Result("complete", null), //
|
|
||||||
new Result("end", null) //
|
|
||||||
);
|
);
|
||||||
|
state.setAnimation(0, "events1", false);
|
||||||
|
run(1 / 60f, 1000);
|
||||||
|
|
||||||
state = newState();
|
setup( // 3
|
||||||
state.setAnimation(0, "events", false);
|
expect("start", 0, 0, 0), //
|
||||||
test(state, 1, 1.01f, //
|
expect("event 0", 0, 0, 0), //
|
||||||
new Result("start", null), //
|
expect("event 14", 0, 30, 30), //
|
||||||
new Result("event", "0"), //
|
expect("event 30", 0, 30, 30), //
|
||||||
new Result("event", "14"), //
|
expect("complete 1", 0, 30, 30), //
|
||||||
new Result("event", "30"), //
|
expect("end", 0, 30, 60) //
|
||||||
new Result("complete", null), //
|
|
||||||
new Result("end", null) //
|
|
||||||
);
|
);
|
||||||
|
state.setAnimation(0, "events1", false);
|
||||||
|
run(30, 1000);
|
||||||
|
|
||||||
state = newState();
|
setup( // 4
|
||||||
state.setAnimation(0, "events", false);
|
expect("start", 0, 0, 0), //
|
||||||
state.addAnimation(0, "events", false, 0);
|
expect("event 0", 0, 0, 0), //
|
||||||
test(state, 0.1f, 3f, //
|
expect("event 14", 0, 1, 1), //
|
||||||
new Result("start", null), //
|
expect("event 30", 0, 1, 1), //
|
||||||
new Result("event", "0"), //
|
expect("complete 1", 0, 1, 1), //
|
||||||
new Result("event", "14"), //
|
expect("end", 0, 1, 2) //
|
||||||
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) //
|
|
||||||
);
|
);
|
||||||
|
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 () {
|
void setup (Result... expectedArray) {
|
||||||
AnimationState state = new AnimationState(stateData);
|
test++;
|
||||||
|
expected.addAll(expectedArray);
|
||||||
|
state = new AnimationState(stateData);
|
||||||
state.addListener(stateListener);
|
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) {
|
void run (float incr, float endTime) {
|
||||||
Array expected = new Array(expectedArray);
|
|
||||||
|
|
||||||
Skeleton skeleton = new Skeleton(skeletonData);
|
Skeleton skeleton = new Skeleton(skeletonData);
|
||||||
|
state.apply(skeleton);
|
||||||
for (int i = 0; i < endTime; i++) {
|
while (time < endTime) {
|
||||||
|
time += incr;
|
||||||
skeleton.update(incr);
|
skeleton.update(incr);
|
||||||
state.update(incr);
|
state.update(incr);
|
||||||
state.apply(skeleton);
|
state.apply(skeleton);
|
||||||
}
|
}
|
||||||
|
actual.clear();
|
||||||
if (expected.equals(actual)) {
|
expected.clear();
|
||||||
actual.clear();
|
if (fail) {
|
||||||
return;
|
System.out.println("Test failed: " + test);
|
||||||
|
System.out.println(buffer);
|
||||||
|
System.exit(0);
|
||||||
}
|
}
|
||||||
int i = 0;
|
System.out.println(buffer);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class Result {
|
Result expect (String name, int animationIndex, float trackTime, float totalTime) {
|
||||||
String eventName;
|
Result result = new Result();
|
||||||
String payload;
|
result.name = name;
|
||||||
|
result.animationIndex = animationIndex;
|
||||||
|
result.trackTime = trackTime;
|
||||||
|
result.totalTime = totalTime;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public Result (String eventName, String payload) {
|
Result actual (String name, TrackEntry entry) {
|
||||||
this.eventName = eventName;
|
Result result = new Result();
|
||||||
this.payload = payload;
|
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 () {
|
public int hashCode () {
|
||||||
final int prime = 31;
|
int result = 31 + animationIndex;
|
||||||
int result = 1;
|
result = 31 * result + name.hashCode();
|
||||||
result = prime * result + ((eventName == null) ? 0 : eventName.hashCode());
|
result = 31 * result + Float.floatToIntBits(totalTime);
|
||||||
result = prime * result + ((payload == null) ? 0 : payload.hashCode());
|
result = 31 * result + Float.floatToIntBits(trackTime);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals (Object obj) {
|
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;
|
Result other = (Result)obj;
|
||||||
if (eventName == null) {
|
if (animationIndex != other.animationIndex) return false;
|
||||||
if (other.eventName != null) return false;
|
if (!name.equals(other.name)) return false;
|
||||||
} else if (!eventName.equals(other.eventName)) return false;
|
if (totalTime != other.totalTime) return false;
|
||||||
if (payload == null) {
|
if (trackTime != other.trackTime) return false;
|
||||||
if (other.payload != null) return false;
|
|
||||||
} else if (!payload.equals(other.payload)) return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString () {
|
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 {
|
static public void main (String[] args) throws Exception {
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
||||||
|
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
|
|
||||||
import com.badlogic.gdx.ApplicationAdapter;
|
import com.badlogic.gdx.ApplicationAdapter;
|
||||||
@ -82,21 +83,25 @@ 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 (int trackIndex, Event event) {
|
public void event (TrackEntry entry, Event event) {
|
||||||
System.out.println(trackIndex + " event: " + state.getCurrent(trackIndex) + ", " + event.getData().getName() + ", "
|
System.out
|
||||||
+ event.getInt());
|
.println(entry.getTrackIndex() + " event: " + entry + ", " + event.getData().getName() + ", " + event.getInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void complete (int trackIndex, int loopCount) {
|
public void complete (TrackEntry entry, int loopCount) {
|
||||||
System.out.println(trackIndex + " complete: " + state.getCurrent(trackIndex) + ", " + loopCount);
|
System.out.println(entry.getTrackIndex() + " complete: " + entry + ", " + loopCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start (int trackIndex) {
|
public void interrupt (TrackEntry entry) {
|
||||||
System.out.println(trackIndex + " start: " + state.getCurrent(trackIndex));
|
System.out.println(entry.getTrackIndex() + " interrupt: " + entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void end (int trackIndex) {
|
public void start (TrackEntry entry) {
|
||||||
System.out.println(trackIndex + " end: " + state.getCurrent(trackIndex));
|
System.out.println(entry.getTrackIndex() + " start: " + entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end (TrackEntry entry) {
|
||||||
|
System.out.println(entry.getTrackIndex() + " end: " + entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import com.badlogic.gdx.utils.IntArray;
|
||||||
import com.badlogic.gdx.utils.Pool;
|
import com.badlogic.gdx.utils.Pool;
|
||||||
import com.badlogic.gdx.utils.Pool.Poolable;
|
import com.badlogic.gdx.utils.Pool.Poolable;
|
||||||
|
|
||||||
@ -40,16 +41,17 @@ public class AnimationState {
|
|||||||
private AnimationStateData data;
|
private AnimationStateData data;
|
||||||
private Array<TrackEntry> tracks = new Array();
|
private Array<TrackEntry> tracks = new Array();
|
||||||
private final Array<Event> events = 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 float timeScale = 1;
|
||||||
|
|
||||||
private Pool<TrackEntry> trackEntryPool = new Pool() {
|
final Pool<TrackEntry> trackEntryPool = new Pool() {
|
||||||
protected Object newObject () {
|
protected Object newObject () {
|
||||||
return new TrackEntry();
|
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 () {
|
public AnimationState () {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,16 +66,19 @@ public class AnimationState {
|
|||||||
TrackEntry current = tracks.get(i);
|
TrackEntry current = tracks.get(i);
|
||||||
if (current == null) continue;
|
if (current == null) continue;
|
||||||
|
|
||||||
|
float currentDelta = delta * current.timeScale;
|
||||||
|
|
||||||
TrackEntry next = current.next;
|
TrackEntry next = current.next;
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
|
// When the next entry's delay is passed, change to it.
|
||||||
float nextTime = current.lastTime - next.delay;
|
float nextTime = current.lastTime - next.delay;
|
||||||
if (nextTime >= 0) {
|
if (nextTime >= 0) {
|
||||||
float nextDelta = delta * next.timeScale;
|
next.time = nextTime + delta * next.timeScale;
|
||||||
next.time = nextTime + nextDelta; // For start event to see correct time.
|
current.time += currentDelta;
|
||||||
current.time += delta * current.timeScale; // For end event to see correct time.
|
|
||||||
setCurrent(i, next);
|
setCurrent(i, next);
|
||||||
next.time -= nextDelta; // Prevent increasing time twice, below.
|
queue.drain();
|
||||||
current = next;
|
if (next.previous != null) next.mixTime += currentDelta;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else if (!current.loop && current.lastTime >= current.endTime) {
|
} else if (!current.loop && current.lastTime >= current.endTime) {
|
||||||
// End non-looping animation when it reaches its end time and there is no next entry.
|
// End non-looping animation when it reaches its end time and there is no next entry.
|
||||||
@ -81,7 +86,7 @@ public class AnimationState {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
current.time += delta * current.timeScale;
|
current.time += currentDelta;
|
||||||
if (current.previous != null) {
|
if (current.previous != null) {
|
||||||
float previousDelta = delta * current.previous.timeScale;
|
float previousDelta = delta * current.previous.timeScale;
|
||||||
current.previous.time += previousDelta;
|
current.previous.time += previousDelta;
|
||||||
@ -92,54 +97,63 @@ public class AnimationState {
|
|||||||
|
|
||||||
public void apply (Skeleton skeleton) {
|
public void apply (Skeleton skeleton) {
|
||||||
Array<Event> events = this.events;
|
Array<Event> events = this.events;
|
||||||
int listenerCount = listeners.size;
|
|
||||||
|
|
||||||
for (int i = 0; i < tracks.size; i++) {
|
for (int i = 0; i < tracks.size; i++) {
|
||||||
TrackEntry current = tracks.get(i);
|
TrackEntry current = tracks.get(i);
|
||||||
if (current == null) continue;
|
if (current == null) continue;
|
||||||
|
|
||||||
events.size = 0;
|
|
||||||
|
|
||||||
float time = current.time;
|
float time = current.time;
|
||||||
float lastTime = current.lastTime;
|
float lastTime = current.lastTime;
|
||||||
float endTime = current.endTime;
|
float endTime = current.endTime;
|
||||||
boolean loop = current.loop;
|
boolean loop = current.loop;
|
||||||
if (!loop && time > endTime) time = endTime;
|
if (!loop && time > endTime) time = endTime;
|
||||||
|
|
||||||
|
float alpha = current.mix;
|
||||||
TrackEntry previous = current.previous;
|
TrackEntry previous = current.previous;
|
||||||
if (previous == null)
|
if (previous != null) {
|
||||||
current.animation.mix(skeleton, lastTime, time, loop, events, current.mix);
|
|
||||||
else {
|
|
||||||
float previousTime = previous.time;
|
float previousTime = previous.time;
|
||||||
if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
|
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) {
|
if (alpha >= 1) {
|
||||||
alpha = 1;
|
alpha = 1;
|
||||||
trackEntryPool.free(previous);
|
queue.queueEnd(current.previous);
|
||||||
current.previous = null;
|
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;
|
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 () {
|
public void clearTracks () {
|
||||||
@ -153,9 +167,9 @@ public class AnimationState {
|
|||||||
TrackEntry current = tracks.get(trackIndex);
|
TrackEntry current = tracks.get(trackIndex);
|
||||||
if (current == null) return;
|
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++)
|
for (int i = 0, n = listeners.size; i < n; i++)
|
||||||
listeners.get(i).end(trackIndex);
|
listeners.get(i).end(current);
|
||||||
|
|
||||||
tracks.set(trackIndex, null);
|
tracks.set(trackIndex, null);
|
||||||
|
|
||||||
@ -180,13 +194,16 @@ public class AnimationState {
|
|||||||
|
|
||||||
private void setCurrent (int index, TrackEntry entry) {
|
private void setCurrent (int index, TrackEntry entry) {
|
||||||
TrackEntry current = expandToIndex(index);
|
TrackEntry current = expandToIndex(index);
|
||||||
|
|
||||||
|
tracks.set(index, entry);
|
||||||
|
|
||||||
|
queue.fireStart(entry);
|
||||||
|
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
TrackEntry previous = current.previous;
|
TrackEntry previous = current.previous;
|
||||||
current.previous = null;
|
current.previous = null;
|
||||||
|
|
||||||
if (current.listener != null) current.listener.end(index);
|
queue.fireInterrupt(current);
|
||||||
for (int i = 0, n = listeners.size; i < n; i++)
|
|
||||||
listeners.get(i).end(index);
|
|
||||||
|
|
||||||
entry.mixDuration = data.getMix(current.animation, entry.animation);
|
entry.mixDuration = data.getMix(current.animation, entry.animation);
|
||||||
if (entry.mixDuration > 0) {
|
if (entry.mixDuration > 0) {
|
||||||
@ -195,19 +212,14 @@ public class AnimationState {
|
|||||||
if (previous != null && current.mixTime / current.mixDuration < 0.5f) {
|
if (previous != null && current.mixTime / current.mixDuration < 0.5f) {
|
||||||
entry.previous = previous;
|
entry.previous = previous;
|
||||||
previous = current;
|
previous = current;
|
||||||
} else
|
} else {
|
||||||
entry.previous = current;
|
entry.previous = current;
|
||||||
|
}
|
||||||
} else
|
} 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) */
|
/** @see #setAnimation(int, Animation, boolean) */
|
||||||
@ -227,6 +239,7 @@ public class AnimationState {
|
|||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
entry.endTime = animation.getDuration();
|
entry.endTime = animation.getDuration();
|
||||||
setCurrent(trackIndex, entry);
|
setCurrent(trackIndex, entry);
|
||||||
|
queue.drain();
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +332,7 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public class TrackEntry implements Poolable {
|
static public class TrackEntry implements Poolable {
|
||||||
|
int index;
|
||||||
TrackEntry next, previous;
|
TrackEntry next, previous;
|
||||||
Animation animation;
|
Animation animation;
|
||||||
boolean loop;
|
boolean loop;
|
||||||
@ -422,37 +436,119 @@ public class AnimationState {
|
|||||||
return time >= endTime;
|
return time >= endTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getTrackIndex () {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString () {
|
public String toString () {
|
||||||
return animation == null ? "<none>" : animation.name;
|
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 {
|
static public interface AnimationStateListener {
|
||||||
/** Invoked when the current animation triggers an event. */
|
/** Invoked when this animation triggers an event. */
|
||||||
public void event (int trackIndex, Event 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. */
|
* @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. */
|
/** Invoked just after this animation is set. */
|
||||||
public void start (int trackIndex);
|
public void start (TrackEntry entry);
|
||||||
|
|
||||||
/** Invoked just before the current animation is replaced. */
|
/** Invoked just after another animation is set. */
|
||||||
public void end (int trackIndex);
|
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 {
|
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