- تم التحرير
Blending between 2 tracks which are running simultaneously
Hi there!
I'm using the Spine-C and Spine-SFML runtimes and I've got two animations set up for a character moving: One where he is running and one where he is walking. Both animations are of equal length and sync up perfectly. What I'm trying to do is play both simultaneously and blend between them based on some scalar value (like the speed the character is moving). The movement speed of the character is based on analogue input so just switching to a different animation with a blend duration won't work (and would also desync the skeleton between the animations). My first thought on how to do this was through some kind of track mixing but looking at the documentation it is not immediately clear to me how to do this, or if it is even possible, hence my question is: Is it?
Thanks!
It's totally possible. In Spine C#, I subclassed TrackEntry to allow a simple weighted blend between two animations in the mixing phase:
public class BlendTreeTrackEntry : TrackEntry {
internal Animation endAnimation;
internal float weight;
public float Weight { get { return weight; } set { weight = value; } }
public override void MixAnimation(Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Spine.Event> events, float alpha)
{
// Mix both animations, depending on the weight.
Animation.Mix(skeleton, lastTime, time, loop, events, alpha * (1 - weight));
endAnimation.Mix(skeleton, lastTime, time, loop, events, alpha * (weight));
}
}
There's a few other bits that make this work in AnimationState, but the general idea is that you mix in both animations at the same time depending on weight. It works pretty well, although I think a true multi-animation and multi-dimensional blend tree is a good feature for the runtimes to support on their own.
I should add that you could also do something like this by controlling the weight of animations in different tracks, but I like to use different track indices for functionally different things (animation layer style).
The Animation mix
method applies an animation X% between the current pose and the animation's pose. If you are applying 2 animations you should usually apply one with apply
(or with mix
passing 1). This poses the whole skeleton. Then when you apply the second animation, you use mix
with the mix you want (between 0 and 1, inclusive). Obviously if the second animation mix is 1, you don't need to apply the first one at all. Note I'm assuming these animations pose the whole skeleton.
The above was using the Animation API, which is the lowest level. The next higher up is AnimationState, which provides more functionality and convenience, though under the covers it is just using the Animation API. You could use 2 tracks, track 0 plays "walk" and track 1 plays "run". When you call setAnimation
for track 1, hold on to the returned TrackEntry. Set the mix
TrackEntry field to 0 for only walk, 1 for only run, and between for a mix. The downside to this approach is AnimationState will always apply both animations, even when mix is 0 or 1. However, it is highly unlikely that this will have a performance impact.
You could also do a hybrid approach, where you use the Animation API for walk/run and then use AnimationState to apply more animations on top.
The mix clandestine has shown mixes the first animation with whatever the current skeleton pose is. This could be anything, but is most likely the pose from the last frame. Mixing from that doesn't make a whole lot of sense, which is why you usually want to use apply
(or mix
with 1) on one animation, then mix the rest on top. When using AnimationState, track 0 is usually an animation that poses the whole skeleton and higher tracks are either mixed with that, or only animate a subset of the skeleton.
Thanks for the info guys! Really helped me understand how tracks and states work.
I managed to do what I wanted by storing a pointer to the TrackEntry returned by setAnimation like you suggested Nate and doing some interpolation over time for smooth transitions. Works really well and is easy to add more stuff upon!
Got another question and figured I might as well use the same thread...
Is there any way to use the default AnimationState system and mix in/out an animation on an empty track? Say for example I have two tracks: Track 0 is used to play a run animation which animates the entire skeleton. Track 1 is used to animate the arms only. When starting an animation on track 1 it's not mixed in (I'm guessing this is intended behaviour) which results in the bones snapping to their starting positions.
Is mixing between tracks like this possible or would it have to be done manually?
TrackEntry has mix
, but doesn't provide a built-in way to change that value over time. You could write something that changes the TrackEntry mix from 0 to 1 to get your track 1 animation to be applied smoothly on top of whatever animation is applied on track 0. Note you may not want to do this if track 0 is not posing the same bones, since ideally AnimationState has all the information to fully pose the skeleton without relying on state from previous frames.
We are working on improvements to AnimationState which we will roll out with the v3 changes, soon. We'll consider providing a feature for mixing in an animation on an empty track.
I'll give writing my own little mix over time class a go then. Native support for such mixing would be awesome though! Thanks for the fast info and for considering it.
Ah, good catch Nate. Of course I should be mixing the entire pose from the first animation!