[libgdx] Added slider (time) timeline.

This commit is contained in:
Nathan Sweet 2025-04-22 18:54:07 -04:00
parent 82ad1691c3
commit ac656fc8a9
3 changed files with 88 additions and 106 deletions

View File

@ -183,7 +183,7 @@ public class Animation {
physicsConstraintInertia, physicsConstraintStrength, physicsConstraintDamping, physicsConstraintMass, //
physicsConstraintWind, physicsConstraintGravity, physicsConstraintMix, physicsConstraintReset, //
sequence, //
sliderMix
sliderTime, sliderMix
}
/** The base class for all timelines. */
@ -512,16 +512,17 @@ public class Animation {
}
}
/** The base class for a {@link CurveTimeline} which sets two properties. */
static abstract public class CurveTimeline2 extends CurveTimeline {
/** The base class for a {@link CurveTimeline} that is a {@link BoneTimeline} and sets two properties. */
static abstract public class BoneTimeline2 extends CurveTimeline implements BoneTimeline {
static public final int ENTRIES = 3;
static final int VALUE1 = 1, VALUE2 = 2;
/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}.
* @param propertyId1 Unique identifier for the first property the timeline modifies.
* @param propertyId2 Unique identifier for the second property the timeline modifies. */
public CurveTimeline2 (int frameCount, int bezierCount, String propertyId1, String propertyId2) {
super(frameCount, bezierCount, propertyId1, propertyId2);
final int boneIndex;
/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}. */
public BoneTimeline2 (int frameCount, int bezierCount, int boneIndex, Property property1, Property property2) {
super(frameCount, bezierCount, property1.ordinal() + "|" + boneIndex, property2.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex;
}
public int getFrameEntries () {
@ -537,21 +538,6 @@ public class Animation {
frames[frame + VALUE1] = value1;
frames[frame + VALUE2] = value2;
}
}
/** An interface for timelines which change the property of a bone. */
static public interface BoneTimeline {
/** The index of the bone in {@link Skeleton#getBones()} that will be changed when this timeline is applied. */
public int getBoneIndex ();
}
static abstract public class BoneTimeline1 extends CurveTimeline1 implements BoneTimeline {
final int boneIndex;
public BoneTimeline1 (int frameCount, int bezierCount, int boneIndex, Property property) {
super(frameCount, bezierCount, property.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex;
}
public int getBoneIndex () {
return boneIndex;
@ -568,11 +554,17 @@ public class Animation {
MixDirection direction);
}
static abstract public class BoneTimeline2 extends CurveTimeline2 implements BoneTimeline {
/** An interface for timelines which change the property of a bone. */
static public interface BoneTimeline {
/** The index of the bone in {@link Skeleton#getBones()} that will be changed when this timeline is applied. */
public int getBoneIndex ();
}
static abstract public class BoneTimeline1 extends CurveTimeline1 implements BoneTimeline {
final int boneIndex;
public BoneTimeline2 (int frameCount, int bezierCount, int boneIndex, Property property1, Property property2) {
super(frameCount, bezierCount, property1.ordinal() + "|" + boneIndex, property2.ordinal() + "|" + boneIndex);
public BoneTimeline1 (int frameCount, int bezierCount, int boneIndex, Property property) {
super(frameCount, bezierCount, property.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex;
}
@ -2165,18 +2157,24 @@ public class Animation {
}
}
/** Changes a path constraint's {@link PathConstraintPose#getPosition()}. */
static public class PathConstraintPositionTimeline extends CurveTimeline1 implements ConstraintTimeline {
static abstract public class ConstraintTimeline1 extends CurveTimeline1 implements ConstraintTimeline {
final int constraintIndex;
public PathConstraintPositionTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, Property.pathConstraintPosition.ordinal() + "|" + constraintIndex);
public ConstraintTimeline1 (int frameCount, int bezierCount, int constraintIndex, Property property) {
super(frameCount, bezierCount, property.ordinal() + "|" + constraintIndex);
this.constraintIndex = constraintIndex;
}
public int getConstraintIndex () {
return constraintIndex;
}
}
/** Changes a path constraint's {@link PathConstraintPose#getPosition()}. */
static public class PathConstraintPositionTimeline extends ConstraintTimeline1 {
public PathConstraintPositionTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, constraintIndex, Property.pathConstraintPosition);
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
MixDirection direction, boolean appliedPose) {
@ -2190,16 +2188,9 @@ public class Animation {
}
/** Changes a path constraint's {@link PathConstraintPose#getSpacing()}. */
static public class PathConstraintSpacingTimeline extends CurveTimeline1 implements ConstraintTimeline {
final int constraintIndex;
static public class PathConstraintSpacingTimeline extends ConstraintTimeline1 {
public PathConstraintSpacingTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, Property.pathConstraintSpacing.ordinal() + "|" + constraintIndex);
this.constraintIndex = constraintIndex;
}
public int getConstraintIndex () {
return constraintIndex;
super(frameCount, bezierCount, constraintIndex, Property.pathConstraintSpacing);
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
@ -2307,19 +2298,10 @@ public class Animation {
}
/** The base class for most {@link PhysicsConstraint} timelines. */
static abstract public class PhysicsConstraintTimeline extends CurveTimeline1 implements ConstraintTimeline {
final int constraintIndex;
static abstract public class PhysicsConstraintTimeline extends ConstraintTimeline1 {
/** @param constraintIndex -1 for all physics constraints in the skeleton. */
public PhysicsConstraintTimeline (int frameCount, int bezierCount, int constraintIndex, Property property) {
super(frameCount, bezierCount, property.ordinal() + "|" + constraintIndex);
this.constraintIndex = constraintIndex;
}
/** The index of the physics constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
* applied, or -1 if all physics constraints in the skeleton will be changed. */
public int getConstraintIndex () {
return constraintIndex;
super(frameCount, bezierCount, constraintIndex, property);
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
@ -2546,17 +2528,27 @@ public class Animation {
}
}
/** Changes a slider's {@link SliderPose#getMix()}. */
static public class SliderMixTimeline extends CurveTimeline1 implements ConstraintTimeline {
final int constraintIndex;
public SliderMixTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, Property.sliderMix.ordinal() + "|" + constraintIndex);
this.constraintIndex = constraintIndex;
/** Changes a slider's {@link SliderPose#getTime()}. */
static public class SliderTimeline extends ConstraintTimeline1 {
public SliderTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, constraintIndex, Property.sliderTime);
}
public int getConstraintIndex () {
return constraintIndex;
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
MixDirection direction, boolean appliedPose) {
var constraint = (Slider)skeleton.constraints.items[constraintIndex];
if (constraint.active) {
SliderPose pose = appliedPose ? constraint.applied : constraint.pose;
pose.time = getAbsoluteValue(time, alpha, blend, pose.time, constraint.data.setup.time);
}
}
}
/** Changes a slider's {@link SliderPose#getMix()}. */
static public class SliderMixTimeline extends ConstraintTimeline1 {
public SliderMixTimeline (int frameCount, int bezierCount, int constraintIndex) {
super(frameCount, bezierCount, constraintIndex, Property.sliderMix);
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,

View File

@ -45,9 +45,9 @@ import com.badlogic.gdx.utils.SerializationException;
import com.esotericsoftware.spine.Animation.AlphaTimeline;
import com.esotericsoftware.spine.Animation.AttachmentTimeline;
import com.esotericsoftware.spine.Animation.BoneTimeline2;
import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.CurveTimeline1;
import com.esotericsoftware.spine.Animation.CurveTimeline2;
import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline;
@ -77,6 +77,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.ShearXTimeline;
import com.esotericsoftware.spine.Animation.ShearYTimeline;
import com.esotericsoftware.spine.Animation.SliderMixTimeline;
import com.esotericsoftware.spine.Animation.SliderTimeline;
import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline;
@ -160,7 +161,8 @@ public class SkeletonBinary extends SkeletonLoader {
static public final int PHYSICS_MIX = 7;
static public final int PHYSICS_RESET = 8;
static public final int SLIDER_MIX = 0;
static public final int SLIDER_TIME = 0;
static public final int SLIDER_MIX = 1;
static public final int CURVE_LINEAR = 0;
static public final int CURVE_STEPPED = 1;
@ -1055,24 +1057,11 @@ public class SkeletonBinary extends SkeletonLoader {
int index = input.readInt(true);
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
switch (type) {
case SLIDER_MIX -> {
var timeline = new SliderMixTimeline(frameCount, bezierCount, index);
float time = input.readFloat(), mix = input.readFloat();
for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
timeline.setFrame(frame, time, mix);
if (frame == frameLast) break;
float time2 = input.readFloat(), mix2 = input.readFloat();
switch (input.readByte()) {
case CURVE_STEPPED -> timeline.setStepped(frame);
case CURVE_BEZIER -> setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
}
time = time2;
mix = mix2;
}
timelines.add(timeline);
}
}
readTimeline(input, timelines, switch (type) {
case SLIDER_TIME -> new SliderTimeline(frameCount, bezierCount, index);
case SLIDER_MIX -> new SliderMixTimeline(frameCount, bezierCount, index);
default -> throw new SerializationException();
}, 1);
}
}
@ -1221,7 +1210,7 @@ public class SkeletonBinary extends SkeletonLoader {
timelines.add(timeline);
}
private void readTimeline (SkeletonInput input, Array<Timeline> timelines, CurveTimeline2 timeline, float scale)
private void readTimeline (SkeletonInput input, Array<Timeline> timelines, BoneTimeline2 timeline, float scale)
throws IOException {
float time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale;
for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {

View File

@ -46,9 +46,9 @@ import com.badlogic.gdx.utils.SerializationException;
import com.esotericsoftware.spine.Animation.AlphaTimeline;
import com.esotericsoftware.spine.Animation.AttachmentTimeline;
import com.esotericsoftware.spine.Animation.BoneTimeline2;
import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.CurveTimeline1;
import com.esotericsoftware.spine.Animation.CurveTimeline2;
import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline;
@ -78,6 +78,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.ShearXTimeline;
import com.esotericsoftware.spine.Animation.ShearYTimeline;
import com.esotericsoftware.spine.Animation.SliderMixTimeline;
import com.esotericsoftware.spine.Animation.SliderTimeline;
import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline;
@ -763,7 +764,7 @@ public class SkeletonJson extends SkeletonLoader {
}
timelines.add(timeline);
}
case "alpha" -> timelines.add(readTimeline(keyMap, new AlphaTimeline(frames, frames, slot.index), 0, 1));
case "alpha" -> readTimeline(timelines, keyMap, new AlphaTimeline(frames, frames, slot.index), 0, 1);
case "rgba2" -> {
var timeline = new RGBA2Timeline(frames, frames * 7, slot.index);
float time = keyMap.getFloat("time", 0);
@ -878,21 +879,17 @@ public class SkeletonJson extends SkeletonLoader {
int frames = timelineMap.size;
switch (timelineMap.name) {
case "rotate" -> timelines.add(readTimeline(keyMap, new RotateTimeline(frames, frames, bone.index), 0, 1));
case "rotate" -> readTimeline(timelines, keyMap, new RotateTimeline(frames, frames, bone.index), 0, 1);
case "translate" -> //
timelines.add(readTimeline(keyMap, new TranslateTimeline(frames, frames << 1, bone.index), "x", "y", 0, scale));
case "translatex" -> //
timelines.add(readTimeline(keyMap, new TranslateXTimeline(frames, frames, bone.index), 0, scale));
case "translatey" -> //
timelines.add(readTimeline(keyMap, new TranslateYTimeline(frames, frames, bone.index), 0, scale));
case "scale" -> timelines
.add(readTimeline(keyMap, new ScaleTimeline(frames, frames << 1, bone.index), "x", "y", 1, 1));
case "scalex" -> timelines.add(readTimeline(keyMap, new ScaleXTimeline(frames, frames, bone.index), 1, 1));
case "scaley" -> timelines.add(readTimeline(keyMap, new ScaleYTimeline(frames, frames, bone.index), 1, 1));
case "shear" -> //
timelines.add(readTimeline(keyMap, new ShearTimeline(frames, frames << 1, bone.index), "x", "y", 0, 1));
case "shearx" -> timelines.add(readTimeline(keyMap, new ShearXTimeline(frames, frames, bone.index), 0, 1));
case "sheary" -> timelines.add(readTimeline(keyMap, new ShearYTimeline(frames, frames, bone.index), 0, 1));
readTimeline(timelines, keyMap, new TranslateTimeline(frames, frames << 1, bone.index), "x", "y", 0, scale);
case "translatex" -> readTimeline(timelines, keyMap, new TranslateXTimeline(frames, frames, bone.index), 0, scale);
case "translatey" -> readTimeline(timelines, keyMap, new TranslateYTimeline(frames, frames, bone.index), 0, scale);
case "scale" -> readTimeline(timelines, keyMap, new ScaleTimeline(frames, frames << 1, bone.index), "x", "y", 1, 1);
case "scalex" -> readTimeline(timelines, keyMap, new ScaleXTimeline(frames, frames, bone.index), 1, 1);
case "scaley" -> readTimeline(timelines, keyMap, new ScaleYTimeline(frames, frames, bone.index), 1, 1);
case "shear" -> readTimeline(timelines, keyMap, new ShearTimeline(frames, frames << 1, bone.index), "x", "y", 0, 1);
case "shearx" -> readTimeline(timelines, keyMap, new ShearXTimeline(frames, frames, bone.index), 0, 1);
case "sheary" -> readTimeline(timelines, keyMap, new ShearYTimeline(frames, frames, bone.index), 0, 1);
case "inherit" -> {
var timeline = new InheritTimeline(frames, bone.index);
for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++) {
@ -997,12 +994,12 @@ public class SkeletonJson extends SkeletonLoader {
switch (timelineMap.name) {
case "position" -> {
var timeline = new PathConstraintPositionTimeline(frames, frames, index);
timelines.add(readTimeline(keyMap, timeline, 0, constraint.positionMode == PositionMode.fixed ? scale : 1));
readTimeline(timelines, keyMap, timeline, 0, constraint.positionMode == PositionMode.fixed ? scale : 1);
}
case "spacing" -> {
var timeline = new PathConstraintSpacingTimeline(frames, frames, index);
timelines.add(readTimeline(keyMap, timeline, 0,
constraint.spacingMode == SpacingMode.length || constraint.spacingMode == SpacingMode.fixed ? scale : 1));
readTimeline(timelines, keyMap, timeline, 0,
constraint.spacingMode == SpacingMode.length || constraint.spacingMode == SpacingMode.fixed ? scale : 1);
}
case "mix" -> {
var timeline = new PathConstraintMixTimeline(frames, frames * 3, index);
@ -1074,7 +1071,7 @@ public class SkeletonJson extends SkeletonLoader {
continue;
}
}
timelines.add(readTimeline(keyMap, timeline, defaultValue, 1));
readTimeline(timelines, keyMap, timeline, defaultValue, 1);
}
}
@ -1089,7 +1086,8 @@ public class SkeletonJson extends SkeletonLoader {
int frames = timelineMap.size;
switch (timelineMap.name) {
case "mix" -> timelines.add(readTimeline(keyMap, new SliderMixTimeline(frames, frames, index), 1, 1));
case "time" -> readTimeline(timelines, keyMap, new SliderTimeline(frames, frames, index), 1, 1);
case "mix" -> readTimeline(timelines, keyMap, new SliderMixTimeline(frames, frames, index), 1, 1);
}
}
}
@ -1231,14 +1229,16 @@ public class SkeletonJson extends SkeletonLoader {
skeletonData.animations.add(new Animation(name, timelines, duration));
}
private Timeline readTimeline (JsonValue keyMap, CurveTimeline1 timeline, float defaultValue, float scale) {
private void readTimeline (Array<Timeline> timelines, JsonValue keyMap, CurveTimeline1 timeline, float defaultValue,
float scale) {
float time = keyMap.getFloat("time", 0), value = keyMap.getFloat("value", defaultValue) * scale;
for (int frame = 0, bezier = 0;; frame++) {
timeline.setFrame(frame, time, value);
JsonValue nextMap = keyMap.next;
if (nextMap == null) {
timeline.shrink(bezier);
return timeline;
timelines.add(timeline);
return;
}
float time2 = nextMap.getFloat("time", 0);
float value2 = nextMap.getFloat("value", defaultValue) * scale;
@ -1250,8 +1250,8 @@ public class SkeletonJson extends SkeletonLoader {
}
}
private Timeline readTimeline (JsonValue keyMap, CurveTimeline2 timeline, String name1, String name2, float defaultValue,
float scale) {
private void readTimeline (Array<Timeline> timelines, JsonValue keyMap, BoneTimeline2 timeline, String name1, String name2,
float defaultValue, float scale) {
float time = keyMap.getFloat("time", 0);
float value1 = keyMap.getFloat(name1, defaultValue) * scale, value2 = keyMap.getFloat(name2, defaultValue) * scale;
for (int frame = 0, bezier = 0;; frame++) {
@ -1259,7 +1259,8 @@ public class SkeletonJson extends SkeletonLoader {
JsonValue nextMap = keyMap.next;
if (nextMap == null) {
timeline.shrink(bezier);
return timeline;
timelines.add(timeline);
return;
}
float time2 = nextMap.getFloat("time", 0);
float nvalue1 = nextMap.getFloat(name1, defaultValue) * scale, nvalue2 = nextMap.getFloat(name2, defaultValue) * scale;