mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[libgdx] Improved AnimationState hold system. Removed holdPrevious, interruptAlpha.
This commit is contained in:
parent
21bb8df68f
commit
f68e498154
@ -1917,9 +1917,6 @@ public class SkeletonSerializer {
|
|||||||
writeTrackEntry(obj.getMixingTo());
|
writeTrackEntry(obj.getMixingTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
json.writeName("holdPrevious");
|
|
||||||
json.writeValue(obj.getHoldPrevious());
|
|
||||||
|
|
||||||
json.writeName("shortestRotation");
|
json.writeName("shortestRotation");
|
||||||
json.writeValue(obj.getShortestRotation());
|
json.writeValue(obj.getShortestRotation());
|
||||||
|
|
||||||
|
|||||||
@ -1673,7 +1673,7 @@ public class Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Fires events for frames > <code>lastTime</code> and <= <code>time</code>. */
|
/** Fires events for frames > <code>lastTime</code> and <= <code>time</code>. */
|
||||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> firedEvents, float alpha,
|
public void apply (@Null Skeleton skeleton, float lastTime, float time, @Null Array<Event> firedEvents, float alpha,
|
||||||
boolean fromSetup, boolean add, boolean out, boolean appliedPose) {
|
boolean fromSetup, boolean add, boolean out, boolean appliedPose) {
|
||||||
if (firedEvents == null) return;
|
if (firedEvents == null) return;
|
||||||
|
|
||||||
|
|||||||
@ -50,39 +50,7 @@ import com.esotericsoftware.spine.Animation.Timeline;
|
|||||||
* See <a href='https://esotericsoftware.com/spine-applying-animations/'>Applying Animations</a> in the Spine Runtimes Guide. */
|
* See <a href='https://esotericsoftware.com/spine-applying-animations/'>Applying Animations</a> in the Spine Runtimes Guide. */
|
||||||
public class AnimationState {
|
public class AnimationState {
|
||||||
static final Animation emptyAnimation = new Animation("<empty>", new Array(true, 0, Timeline[]::new), 0);
|
static final Animation emptyAnimation = new Animation("<empty>", new Array(true, 0, Timeline[]::new), 0);
|
||||||
|
static private final int SUBSEQUENT = 0, FIRST = 1, HOLD = 2, HOLD_FIRST = 3, SETUP = 1, CURRENT = 2;
|
||||||
/** 1) A previously applied timeline has set this property.<br>
|
|
||||||
* Result: Mix from the current pose to the timeline pose. */
|
|
||||||
static private final int SUBSEQUENT = 0;
|
|
||||||
/** 1) This is the first timeline to set this property.<br>
|
|
||||||
* 2) The next track entry applied after this one does not have a timeline to set this property.<br>
|
|
||||||
* Result: Mix from the setup pose to the timeline pose. */
|
|
||||||
static private final int FIRST = 1;
|
|
||||||
/** 1) A previously applied timeline has set this property.<br>
|
|
||||||
* 2) The next track entry to be applied does have a timeline to set this property.<br>
|
|
||||||
* 3) The next track entry after that one does not have a timeline to set this property.<br>
|
|
||||||
* Result: Mix from the current pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading
|
|
||||||
* animations that key the same property. A subsequent timeline will set this property using a mix. */
|
|
||||||
static private final int HOLD_SUBSEQUENT = 2;
|
|
||||||
/** 1) This is the first timeline to set this property.<br>
|
|
||||||
* 2) The next track entry to be applied does have a timeline to set this property.<br>
|
|
||||||
* 3) The next track entry after that one does not have a timeline to set this property.<br>
|
|
||||||
* Result: Mix from the setup pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading animations
|
|
||||||
* that key the same property. A subsequent timeline will set this property using a mix. */
|
|
||||||
static private final int HOLD_FIRST = 3;
|
|
||||||
/** 1) This is the first timeline to set this property.<br>
|
|
||||||
* 2) The next track entry to be applied does have a timeline to set this property.<br>
|
|
||||||
* 3) The next track entry after that one does have a timeline to set this property.<br>
|
|
||||||
* 4) timelineHoldMix stores the first subsequent track entry that does not have a timeline to set this property.<br>
|
|
||||||
* Result: The same as HOLD except the mix percentage from the timelineHoldMix track entry is used. This handles when more than
|
|
||||||
* 2 track entries in a row have a timeline that sets the same property.<br>
|
|
||||||
* Eg, A -> B -> C -> D where A, B, and C have a timeline setting same property, but D does not. When A is applied, to avoid
|
|
||||||
* "dipping" A is not mixed out, however D (the first entry that doesn't set the property) mixing in is used to mix out A
|
|
||||||
* (which affects B and C). Without using D to mix out, A would be applied fully until mixing completes, then snap to the mixed
|
|
||||||
* out position. */
|
|
||||||
static private final int HOLD_MIX = 4;
|
|
||||||
|
|
||||||
static private final int SETUP = 1, CURRENT = 2;
|
|
||||||
|
|
||||||
private AnimationStateData data;
|
private AnimationStateData data;
|
||||||
final Array<TrackEntry> tracks = new Array(true, 4, TrackEntry[]::new);
|
final Array<TrackEntry> tracks = new Array(true, 4, TrackEntry[]::new);
|
||||||
@ -184,7 +152,10 @@ public class AnimationState {
|
|||||||
if (from.totalAlpha == 0 || to.mixDuration == 0) {
|
if (from.totalAlpha == 0 || to.mixDuration == 0) {
|
||||||
to.mixingFrom = from.mixingFrom;
|
to.mixingFrom = from.mixingFrom;
|
||||||
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
|
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
|
||||||
to.interruptAlpha = from.interruptAlpha;
|
if (from.totalAlpha == 0) {
|
||||||
|
for (TrackEntry next = to; next.mixingTo != null; next = next.mixingTo)
|
||||||
|
next.keepHold = true;
|
||||||
|
}
|
||||||
queue.end(from);
|
queue.end(from);
|
||||||
}
|
}
|
||||||
return finished;
|
return finished;
|
||||||
@ -243,7 +214,7 @@ public class AnimationState {
|
|||||||
: current.timelinesRotation.items;
|
: current.timelinesRotation.items;
|
||||||
for (int ii = 0; ii < timelineCount; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
Timeline timeline = timelines[ii];
|
Timeline timeline = timelines[ii];
|
||||||
boolean fromSetup = timelineMode[ii] == FIRST;
|
boolean fromSetup = (timelineMode[ii] & FIRST) != 0;
|
||||||
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
||||||
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, ii << 1,
|
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, ii << 1,
|
||||||
firstFrame);
|
firstFrame);
|
||||||
@ -279,20 +250,12 @@ public class AnimationState {
|
|||||||
|
|
||||||
private float applyMixingFrom (TrackEntry to, Skeleton skeleton) {
|
private float applyMixingFrom (TrackEntry to, Skeleton skeleton) {
|
||||||
TrackEntry from = to.mixingFrom;
|
TrackEntry from = to.mixingFrom;
|
||||||
if (from.mixingFrom != null) applyMixingFrom(from, skeleton);
|
float fromMix = from.mixingFrom != null ? applyMixingFrom(from, skeleton) : 1;
|
||||||
|
float mix = to.mixDuration == 0 ? 1 : Math.min(1, to.mixTime / to.mixDuration);
|
||||||
float mix;
|
|
||||||
if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
|
|
||||||
mix = 1;
|
|
||||||
else {
|
|
||||||
mix = to.mixTime / to.mixDuration;
|
|
||||||
if (mix > 1) mix = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean attachments = mix < from.mixAttachmentThreshold, drawOrder = mix < from.mixDrawOrderThreshold;
|
boolean attachments = mix < from.mixAttachmentThreshold, drawOrder = mix < from.mixDrawOrderThreshold;
|
||||||
int timelineCount = from.animation.timelines.size;
|
int timelineCount = from.animation.timelines.size;
|
||||||
Timeline[] timelines = from.animation.timelines.items;
|
Timeline[] timelines = from.animation.timelines.items;
|
||||||
float alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
float alphaMix = from.alpha * fromMix * (1 - mix), keep = 1 - mix * to.alpha, alphaHold = keep > 0 ? alphaMix / keep : 0;
|
||||||
float animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
|
float animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
|
||||||
Array<Event> events = null;
|
Array<Event> events = null;
|
||||||
if (from.reverse)
|
if (from.reverse)
|
||||||
@ -307,33 +270,17 @@ public class AnimationState {
|
|||||||
from.totalAlpha = 0;
|
from.totalAlpha = 0;
|
||||||
for (int i = 0; i < timelineCount; i++) {
|
for (int i = 0; i < timelineCount; i++) {
|
||||||
Timeline timeline = timelines[i];
|
Timeline timeline = timelines[i];
|
||||||
boolean fromSetup;
|
int mode = timelineMode[i];
|
||||||
float alpha;
|
float alpha;
|
||||||
switch (timelineMode[i]) {
|
if ((mode & HOLD) != 0) {
|
||||||
case SUBSEQUENT -> {
|
|
||||||
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
|
||||||
fromSetup = false;
|
|
||||||
alpha = alphaMix;
|
|
||||||
}
|
|
||||||
case FIRST -> {
|
|
||||||
fromSetup = true;
|
|
||||||
alpha = alphaMix;
|
|
||||||
}
|
|
||||||
case HOLD_SUBSEQUENT -> {
|
|
||||||
fromSetup = false;
|
|
||||||
alpha = alphaHold;
|
|
||||||
}
|
|
||||||
case HOLD_FIRST -> {
|
|
||||||
fromSetup = true;
|
|
||||||
alpha = alphaHold;
|
|
||||||
}
|
|
||||||
default -> { // HOLD_MIX
|
|
||||||
fromSetup = true;
|
|
||||||
TrackEntry holdMix = timelineHoldMix[i];
|
TrackEntry holdMix = timelineHoldMix[i];
|
||||||
alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
|
alpha = holdMix == null ? alphaHold : alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
|
||||||
}
|
} else {
|
||||||
|
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
||||||
|
alpha = alphaMix;
|
||||||
}
|
}
|
||||||
from.totalAlpha += alpha;
|
from.totalAlpha += alpha;
|
||||||
|
boolean fromSetup = (mode & FIRST) != 0;
|
||||||
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
||||||
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, i << 1, firstFrame);
|
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, i << 1, firstFrame);
|
||||||
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||||
@ -522,11 +469,6 @@ public class AnimationState {
|
|||||||
current.mixingFrom = from;
|
current.mixingFrom = from;
|
||||||
from.mixingTo = current;
|
from.mixingTo = current;
|
||||||
current.mixTime = 0;
|
current.mixTime = 0;
|
||||||
|
|
||||||
// Store the interrupted mix percentage.
|
|
||||||
if (from.mixingFrom != null && from.mixDuration > 0)
|
|
||||||
current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration);
|
|
||||||
|
|
||||||
from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
|
from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,7 +565,7 @@ public class AnimationState {
|
|||||||
* {@link #setEmptyAnimations(float)}, or {@link #addEmptyAnimation(int, float, float)}. Mixing to an empty animation causes
|
* {@link #setEmptyAnimations(float)}, or {@link #addEmptyAnimation(int, float, float)}. Mixing to an empty animation causes
|
||||||
* the previous animation to be applied less and less over the mix duration. Properties keyed in the previous animation
|
* the previous animation to be applied less and less over the mix duration. Properties keyed in the previous animation
|
||||||
* transition to the value from lower tracks or to the setup pose value if no lower tracks key the property. A mix duration of
|
* transition to the value from lower tracks or to the setup pose value if no lower tracks key the property. A mix duration of
|
||||||
* 0 still needs to be applied one more time to mix out, so the the properties it was animating are reverted.
|
* 0 still needs to be applied one more time to mix out, so the properties it was animating are reverted.
|
||||||
* <p>
|
* <p>
|
||||||
* Mixing in is done by first setting an empty animation, then adding an animation using
|
* Mixing in is done by first setting an empty animation, then adding an animation using
|
||||||
* {@link #addAnimation(int, Animation, boolean, float)} with the desired delay (an empty animation has a duration of 0) and on
|
* {@link #addAnimation(int, Animation, boolean, float)} with the desired delay (an empty animation has a duration of 0) and on
|
||||||
@ -689,7 +631,6 @@ public class AnimationState {
|
|||||||
entry.trackIndex = trackIndex;
|
entry.trackIndex = trackIndex;
|
||||||
entry.animation = animation;
|
entry.animation = animation;
|
||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
entry.holdPrevious = false;
|
|
||||||
|
|
||||||
entry.additive = false;
|
entry.additive = false;
|
||||||
entry.reverse = false;
|
entry.reverse = false;
|
||||||
@ -715,8 +656,8 @@ public class AnimationState {
|
|||||||
entry.alpha = 1;
|
entry.alpha = 1;
|
||||||
entry.mixTime = 0;
|
entry.mixTime = 0;
|
||||||
entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
|
entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
|
||||||
entry.interruptAlpha = 1;
|
|
||||||
entry.totalAlpha = 0;
|
entry.totalAlpha = 0;
|
||||||
|
entry.keepHold = false;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,40 +697,45 @@ public class AnimationState {
|
|||||||
entry.timelineHoldMix.clear();
|
entry.timelineHoldMix.clear();
|
||||||
TrackEntry[] timelineHoldMix = entry.timelineHoldMix.setSize(timelinesCount);
|
TrackEntry[] timelineHoldMix = entry.timelineHoldMix.setSize(timelinesCount);
|
||||||
ObjectSet<String> propertyIds = this.propertyIds;
|
ObjectSet<String> propertyIds = this.propertyIds;
|
||||||
boolean holdPrevious = false, add = entry.additive;
|
boolean add = entry.additive, keepHold = entry.keepHold;
|
||||||
TrackEntry to = entry.mixingTo;
|
TrackEntry to = entry.mixingTo;
|
||||||
if (to != null) {
|
|
||||||
if (to.additive)
|
|
||||||
to = null;
|
|
||||||
else
|
|
||||||
holdPrevious = to.holdPrevious;
|
|
||||||
}
|
|
||||||
outer:
|
outer:
|
||||||
for (int i = 0; i < timelinesCount; i++) {
|
for (int i = 0; i < timelinesCount; i++) {
|
||||||
Timeline timeline = timelines[i];
|
Timeline timeline = timelines[i];
|
||||||
String[] ids = timeline.propertyIds;
|
String[] ids = timeline.propertyIds;
|
||||||
boolean first = propertyIds.addAll(ids)
|
boolean first = propertyIds.addAll(ids)
|
||||||
&& !(timeline instanceof DrawOrderFolderTimeline && propertyIds.contains(DrawOrderTimeline.propertyID));
|
&& !(timeline instanceof DrawOrderFolderTimeline && propertyIds.contains(DrawOrderTimeline.propertyID));
|
||||||
if (add && timeline.additive)
|
|
||||||
|
if (add && timeline.additive) {
|
||||||
timelineMode[i] = first ? FIRST : SUBSEQUENT;
|
timelineMode[i] = first ? FIRST : SUBSEQUENT;
|
||||||
else if (!first)
|
continue;
|
||||||
timelineMode[i] = holdPrevious ? HOLD_SUBSEQUENT : SUBSEQUENT;
|
|
||||||
else if (holdPrevious)
|
|
||||||
timelineMode[i] = HOLD_FIRST;
|
|
||||||
else if (to == null || timeline.instant || !to.animation.hasTimeline(ids))
|
|
||||||
timelineMode[i] = FIRST;
|
|
||||||
else {
|
|
||||||
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
|
||||||
if (next.animation.hasTimeline(ids)) continue;
|
|
||||||
if (next.mixDuration > 0) {
|
|
||||||
timelineMode[i] = HOLD_MIX;
|
|
||||||
timelineHoldMix[i] = next;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
timelineMode[i] = HOLD_FIRST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (TrackEntry from = entry.mixingFrom; from != null; from = from.mixingFrom) {
|
||||||
|
if (from.animation.hasTimeline(ids)) {
|
||||||
|
// An earlier entry on this track keys this property, isolating it from lower tracks.
|
||||||
|
timelineMode[i] = SUBSEQUENT;
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hold if the next entry will overwrite this property.
|
||||||
|
int mode;
|
||||||
|
if (to == null || timeline.instant || (to.additive && timeline.additive) || !to.animation.hasTimeline(ids))
|
||||||
|
mode = first ? FIRST : SUBSEQUENT;
|
||||||
|
else {
|
||||||
|
mode = first ? HOLD_FIRST : HOLD;
|
||||||
|
// Find next entry that doesn't overwrite this property. Its mix fades out the hold, instead of it ending abruptly.
|
||||||
|
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
||||||
|
if ((next.additive && timeline.additive) || !next.animation.hasTimeline(ids)) {
|
||||||
|
if (next.mixDuration > 0) timelineHoldMix[i] = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keepHold) mode = (mode & ~HOLD) | (timelineMode[i] & HOLD);
|
||||||
|
timelineMode[i] = mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,12 +817,17 @@ public class AnimationState {
|
|||||||
@Null TrackEntry previous, next, mixingFrom, mixingTo;
|
@Null TrackEntry previous, next, mixingFrom, mixingTo;
|
||||||
@Null AnimationStateListener listener;
|
@Null AnimationStateListener listener;
|
||||||
int trackIndex;
|
int trackIndex;
|
||||||
boolean loop, holdPrevious, additive, reverse, shortestRotation;
|
boolean loop, additive, reverse, shortestRotation, keepHold;
|
||||||
float eventThreshold, mixAttachmentThreshold, alphaAttachmentThreshold, mixDrawOrderThreshold;
|
float eventThreshold, mixAttachmentThreshold, alphaAttachmentThreshold, mixDrawOrderThreshold;
|
||||||
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
||||||
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
||||||
float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
|
float alpha, mixTime, mixDuration, totalAlpha;
|
||||||
|
|
||||||
|
/** For each timeline:
|
||||||
|
* <li>Bit 0, FIRST: 0 = mix from current pose, 1 = mix from setup pose. Timeline is first to set the property.
|
||||||
|
* <li>Bit 1, HOLD: 0 = mix out using alphaMix, 1 = apply full alpha to prevent dipping. Timeline is first on its track to
|
||||||
|
* set the property and the next entry (mixingTo) also sets it. When held, timelineHoldMix's mix controls how the hold fades
|
||||||
|
* out (for 3+ entry chains where the chain eventually stops setting the property). */
|
||||||
final IntArray timelineMode = new IntArray();
|
final IntArray timelineMode = new IntArray();
|
||||||
final Array<TrackEntry> timelineHoldMix = new Array(true, 8, TrackEntry[]::new);
|
final Array<TrackEntry> timelineHoldMix = new Array(true, 8, TrackEntry[]::new);
|
||||||
final FloatArray timelinesRotation = new FloatArray();
|
final FloatArray timelinesRotation = new FloatArray();
|
||||||
@ -1167,9 +1118,8 @@ public class AnimationState {
|
|||||||
/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
|
/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
|
||||||
* {@link AnimationStateData#getMix(Animation, Animation)} based on the animation before this animation (if any).
|
* {@link AnimationStateData#getMix(Animation, Animation)} based on the animation before this animation (if any).
|
||||||
* <p>
|
* <p>
|
||||||
* A mix duration of 0 still needs to be applied one more time to mix out, so the the properties it was animating are
|
* A mix duration of 0 still needs to be applied one more time to mix out, so the properties it was animating are reverted.
|
||||||
* reverted. A mix duration of 0 can be set at any time to end the mix on the next {@link AnimationState#update(float)
|
* A mix duration of 0 can be set at any time to end the mix on the next {@link AnimationState#update(float) update}.
|
||||||
* update}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The <code>mixDuration</code> can be set manually rather than use the value from
|
* The <code>mixDuration</code> can be set manually rather than use the value from
|
||||||
* {@link AnimationStateData#getMix(Animation, Animation)}. In that case, the <code>mixDuration</code> can be set for a new
|
* {@link AnimationStateData#getMix(Animation, Animation)}. In that case, the <code>mixDuration</code> can be set for a new
|
||||||
@ -1232,25 +1182,6 @@ public class AnimationState {
|
|||||||
return mixingTo;
|
return mixingTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHoldPrevious (boolean holdPrevious) {
|
|
||||||
this.holdPrevious = holdPrevious;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead
|
|
||||||
* of being mixed out.
|
|
||||||
* <p>
|
|
||||||
* When mixing between animations that key the same property, if a lower track also keys that property then the value will
|
|
||||||
* briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0%
|
|
||||||
* while the second animation mixes from 0% to 100%. Setting <code>holdPrevious</code> to true applies the first animation
|
|
||||||
* at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which
|
|
||||||
* keys the property, only when a higher track also keys the property.
|
|
||||||
* <p>
|
|
||||||
* Snapping will occur if <code>holdPrevious</code> is true and this animation does not key all the same properties as the
|
|
||||||
* previous animation. */
|
|
||||||
public boolean getHoldPrevious () {
|
|
||||||
return holdPrevious;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShortestRotation (boolean shortestRotation) {
|
public void setShortestRotation (boolean shortestRotation) {
|
||||||
this.shortestRotation = shortestRotation;
|
this.shortestRotation = shortestRotation;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,10 +211,8 @@ public class SkeletonViewer extends ApplicationAdapter {
|
|||||||
state.setEmptyAnimation(track, 0);
|
state.setEmptyAnimation(track, 0);
|
||||||
entry = state.addAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked(), 0);
|
entry = state.addAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked(), 0);
|
||||||
entry.setMixDuration(ui.mixSlider.getValue());
|
entry.setMixDuration(ui.mixSlider.getValue());
|
||||||
} else {
|
} else
|
||||||
entry = state.setAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());
|
entry = state.setAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());
|
||||||
entry.setHoldPrevious(track > 0 && ui.holdPrevCheckbox.isChecked());
|
|
||||||
}
|
|
||||||
entry.setAdditive(track > 0 && ui.addCheckbox.isChecked());
|
entry.setAdditive(track > 0 && ui.addCheckbox.isChecked());
|
||||||
entry.setReverse(ui.reverseCheckbox.isChecked());
|
entry.setReverse(ui.reverseCheckbox.isChecked());
|
||||||
entry.setAlpha(ui.alphaSlider.getValue());
|
entry.setAlpha(ui.alphaSlider.getValue());
|
||||||
|
|||||||
@ -570,10 +570,7 @@ class SkeletonViewerUI {
|
|||||||
if (current != null) {
|
if (current != null) {
|
||||||
loopCheckbox.setChecked(current.getLoop());
|
loopCheckbox.setChecked(current.getLoop());
|
||||||
reverseCheckbox.setChecked(current.getReverse());
|
reverseCheckbox.setChecked(current.getReverse());
|
||||||
if (track > 0) {
|
if (track > 0) addCheckbox.setChecked(current.getAdditive());
|
||||||
addCheckbox.setChecked(current.getAdditive());
|
|
||||||
holdPrevCheckbox.setChecked(current.getHoldPrevious());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user