• Editor
  • [Probably Easy] Animation mixing with multiple tracks

Related Discussions
...

I have 3 animations:
1) "Run" animation, that is on Track 0 and loops. This keys ALL bones and has keys at the beginning and the end.
2) "Raise_Sword" animation, that will be set on Track 1. This keys only the arm-bones, and has keys at the beginning and end.
3) "Swing_Sword" animation, that will be set on Track 1 after Raise_Sword has completed. This keys only the arm-bones and has keys at the beginning and end.

The issue: With "Run" looping on Track 0, any time I transition between two animations on Track 1 (going from Raise_Sword to Swing_Sword), the mix on the arm positions is including the Run animations position (so the arms sway towards where they would be during the Run animation momentarily).

Is this normal? I would think with Raise_Sword and Swing_Sword being on a higher track, and with the arm bones keyed at both the beginning and end, that the mix would be between them only, and not include the lower track.

Any suggestions for what to do? Do I need to split the Run animation into two animations, and have the arms-only portion play on Track 1, and everything-but-arms play on Track 0? If that's the case, I'd have to do extra work to synchronize the arms back with the other bones when the character returns to only running.

Is there any way for the higher track mix to not include any lower tracks during the transition?

Thanks as always! :heart:

It might help to show the AnimationState setAnimation and AnimationState addAnimation calls you are making.

Jamez0r wrote

Is there any way for the higher track mix to not include any lower tracks during the transition?

Yes, apply the higher track without transitioning from/to an empty animation. When mixing an animation on a higher track from an empty animation, you are mixing from the pose of the empty animation (which, since it is empty, is the pose of the lower track) to your higher track animation. If you don't want that, just set the higher track animation without mixing from anything. This will cause the higher track animation to be applied fully immediately. The same logic applies when mixing an animation out on a higher track.

Nate wrote

It might help to show the AnimationState setAnimation and AnimationState addAnimation calls you are making.

Jamez0r wrote

Is there any way for the higher track mix to not include any lower tracks during the transition?

Yes, apply the higher track without transitioning from/to an empty animation. When mixing an animation on a higher track from an empty animation, you are mixing from the pose of the empty animation (which, since it is empty, is the pose of the lower track) to your higher track animation. If you don't want that, just set the higher track animation without mixing from anything. This will cause the higher track animation to be applied fully immediately. The same logic applies when mixing an animation out on a higher track.

Thanks Nate - the problem only happens when going between animations on track 1. Going from nothing on track 1, to the Raise_Sword animation is fine, and going from Swing_Sword back to nothing on track 1 is fine. Its when going from Raise_Sword to Swing_Sword, both on track 1, that I get the sway back towards the arm position in the "Run" animation on track 0. Either that, or it is swaying back to the Setup Pose position, the arm position in Setup Pose is close to where it is in the Run animation.

At Runtime, I'm applying the animations like this:

1) setAnimation(0, "Run", true);

2) [Player presses and holds mouse button]
addEmptyAnimation(1, 0.1f, 0);
addAnimation(1, "Raise_Sword", false, 0); //The character raises its sword and sits at the last frame of the animation until player lets go of mouse

3) [Player releases mouse button]
setAnimation(1, "Swing_Sword", false);

When the Swing_Sword animation starts, the arms are swaying either towards the position they would be during the Run animation, or the Setup Pose. It happens both at runtime, and if I use the Preview window in Spine.

Preview window in Spine:
1) Set track 0 to "Run", looping
2) Set track 1 to "Raise_Sword", not looping, mix 0.25 //Sword is raised overhead and sits there
3) Set track 1 to "Swing_Sword" just by clicking the "Swing_Sword" animation //Arms sway towards the position in the Run animation for a moment before completing the swing animation

If I remove the "Run" animation from Track 0 in the Preview Window:
2) Set track 1 to "Raise_Sword", not looping, mix 0.25 //Sword is raised overhead and sits there
3) Set track 1 to "Swing_Sword" just by selecting the "Swing_Sword" animation //Arms go directly from the Raise_Sword position to the Swing_Sword position

Not sure if the Preview window showing the same issue is helpful or not. Thanks for all the help :*

Your Raise_Sword and Swing_Sword animations need to key EVERY bone value that you want to override in the run animation, from the start of the animation to the end.

For example, if Swing_Sword keys just the shoulder rotation but on the elbow rotation, it will instead use the elbow rotation from the run animation.
The Preview window is there for you to check in the editor that all the things you wanted to override are correct.

If this doesn't capture the problem, let us know.

Pharan wrote

Your Raise_Sword and Swing_Sword animations need to key EVERY bone value that you want to override in the run animation, from the start of the animation to the end.

For example, if Swing_Sword keys just the shoulder rotation but on the elbow rotation, it will instead use the elbow rotation from the run animation.
The Preview window is there for you to check in the editor that all the things you wanted to override are correct.

If this doesn't capture the problem, let us know.

Thanks Pharan - I went ahead and made a little video that shows my problem. I simplified it to just arm movement to show the issue, so it isn't using the run/raise/swing animations I was talking about. Also, sorry that the sound volume is a bit low :wonder:

I guess my question now is -> does the preview window automatically add empty animations before/after when you go from one animation being selected to another? If so, then I shouldn't be using the preview window to try to solve my issue.

Jamez0r wrote

does the preview window automatically add empty animations before/after when you go from one animation being selected to another?

It uses an empty animation to mix in an animation from no animation selected, and to mix out an animation to no animation selected. It doesn't use an empty animation when changing from one animation to another.

In your video you have a mix duration of 0.25 when transitioning from topleft to topright. Using a mix duration of 0 makes the problem go away.

AnimationState is what is orchestrating the mixing. It is extremely complicated and has to handle many edge cases. The "dipping" you seem to be encountering was a problem previously, but has been fixed for a long time, at least for track 0. Off the top of my head I can't tell if the behavior you see is 1) correct because AnimationState has to work that way and can't fix the dipping for tracks > 0, 2) a bug in AnimationState or it should otherwise not be dipping, or 3) some problem with the animation, mix duration, or something else. It would help if you could post or email your project. contact@esotericsoftware.com

Nate wrote
Jamez0r wrote

does the preview window automatically add empty animations before/after when you go from one animation being selected to another?

It uses an empty animation to mix in an animation from no animation selected, and to mix out an animation to no animation selected. It doesn't use an empty animation when changing from one animation to another.

In your video you have a mix duration of 0.25 when transitioning from topleft to topright. Using a mix duration of 0 makes the problem go away.

AnimationState is what is orchestrating the mixing. It is extremely complicated and has to handle many edge cases. The "dipping" you seem to be encountering was a problem previously, but has been fixed for a long time, at least for track 0. Off the top of my head I can't tell if the behavior you see is 1) correct because AnimationState has to work that way and can't fix the dipping for tracks > 0, 2) a bug in AnimationState or it should otherwise not be dipping, or 3) some problem with the animation, mix duration, or something else. It would help if you could post or email your project. contact@esotericsoftware.com

Thanks Nate, using a mix duration of 0 makes the arm snap instantly to the topleft / topright position. I'd still like it to smoothly transition, am I doing something wrong there?

I created the most basic project that shows my issue - just a torso with an arm. I created 3 animations, setup the same way as the video. Uploading it here.

Thanks for the help, and hopefully I'm not missing/forgetting something silly.

You aren't doing anything wrong. After digging into it more, you have encountered an AnimationState limitation. If a lower track keys something, higher tracks are layered on top. The drawback is that transitioning from one animation to another on a higher track will cause the dipping problem. If you have A on track 0, then on track 1 go from B to C, what happens is B is applied less and less over time while C is applied more and more. At first, when B is just less than fully applied and C is barely applied, you'll see a mix of mostly B and a little A. The smaller the distance between the keyed valued of B and A, the less dip you'll see. Eventually C takes over and you see only C.

AnimationState makes a great effort to avoid dipping on the lowest track that keys something (doesn't have to be track 0, but often it is). Eg, A -> B on track 3 will not dip if lower tracks haven't keyed the same properties. Unfortunately due to how we want animations on higher tracks to be mixed with lower tracks, the dipping problem can't be avoided if lower tracks keyed the same properties. We have tried many things, but it is difficult to fix without breaking something else. AnimationState needs to be stable, eg if you click a bunch of animations in the Preview, it always smoothly transitions without snapping. So far some solutions have worked to avoid dipping on higher tracks, but cause snapping in certain cases, which is unacceptable.

Dipping occurs during mixing, so there is no dipping with a zero TrackEntry mixDuration. As you noted, this causes snapping. I wish we had a better solution! It is something we'd like to improve, but given our current road map, I don't think we can get to it any time soon.

Thanks for the in depth reply!

I'll figure out a workaround for it, either by using an additional track, or splitting the Track 0 animation up into 2 animations (arms on track 1 and everything else on track 0).

This problem continued to bother us so we worked out a limited solution:
Added TrackEntry#holdPrevious to avoid dipping in higher tracks.@9b259c6
This will be ported to the other runtimes soon. It adds TrackEntry holdPrevious, whose function and drawbacks are described here:
spine-runtimes/AnimationState.java at 9b259c66e03fb08462abb6372a59cb1193b3bfdf

To use it, if you have animation A on track 0 and a B->C mix on track 1, when you set animation C you would set holdPrevious to true to prevent dipping between B and C. C should key all the bones that B does to avoid snapping after the mix. Eg:

state.setAnimation(0, "A", true);
state.setAnimation(1, "B", true);
// Later:
TrackEntry entry = state.setAnimation(1, "C", true);
entry.setMixDuration(0.3f);
entry.setHoldPrevious(true);
Nate wrote

This problem continued to bother us so we worked out a limited solution:
Added TrackEntry#holdPrevious to avoid dipping in higher tracks.@9b259c6
This will be ported to the other runtimes soon. It adds TrackEntry holdPrevious, whose function and drawbacks are described here:
spine-runtimes/AnimationState.java at 9b259c66e03fb08462abb6372a59cb1193b3bfdf

To use it, if you have animation A on track 0 and a B->C mix on track 1, when you set animation C you would set holdPrevious to true to prevent dipping between B and C. C should key all the bones that B does to avoid snapping after the mix. Eg:

state.setAnimation(0, "A", true);
state.setAnimation(1, "B", true);
// Later:
TrackEntry entry = state.setAnimation(1, "C", true);
entry.setMixDuration(0.3f);
entry.setHoldPrevious(true);

Ooo, thats perfect! You guys are the best!

You said "will be ported to the other runtimes soon" -> does that mean its available for LibGDX right now (or tomorrow via the nightly?) :o

For libgdx, it is available now (via source, or the next nightly, or maybe already).

Nate wrote

For libgdx, it is available now (via source, or the next nightly, or maybe already).

Thanks a ton!