- Edited
Play backwards?
Just a quick question, how can an animation be played backwards?
I have an animation in which a mecha opens up to let the pilot dismount. Instead of animating the closing animation for the mecha, I'd like to play the opening animation backwards from the end.
Is it possible?
Thanks in advance.
It's possible but not straightforward.
Spine.AnimationState doesn't support it. You would have to implement it yourself, track time and completion and call Animation.Apply(skeleton) yourself.
I recommend just animating the closing animation. The motion would look much better that way anyway.
Understood. I'm not a great coder anyway. My inner lazy animator was hoping for an easy solution XD I guess he'll be disappointed.
Thanks as always Pharan, you're the best!
Here's a kitten for you
Loading Image
pixelmeat wroteThanks as always Pharan, you're the best!
:wonder:
@[deleted]
That was obviously a typo. pixelmeat misspelled "BinaryCats".
Hi,
should it be possible using Mecanim? I'm kind of new to Spine so still figuring out the ins and outs.
Just tried playing an animation backwards and it just freezes. I guess it's internally related to what you're talking about in this thread?
Any advice? Or should I just copy paste the animation and animate it backwards in spine?
Cheers,
Felix
Again, its not supported. The best thing perhaps you can do is to create new animations :sweat: :S
Imagine you have an animation that keys on an attachment. When you play this animation back, does it key it off? does it remain on. You may want both to happen in different scenarios
copy the animation, then use the animation scale feature to reverse the key order.
Yep, Xelnath means to box select your keys in the dopesheet, then drag the box selection edge past the other edge, so all the keys flip over.
Was just looking for stuff like this... except this is exponentially more complicated because we have a fighting game where the player can block enemy attacks and I was hoping to stop and reverse the current attack animation back to .1 normalized and then to blocked state at 2x speed or something that looked good.
Sounds like we will have to take every single attack for all enemies and re-create reverse versions and then just play them at the current 1-normalized point in the attack animation.
It's only the AnimationState API that can't play backward. You can use the Animation API to play things backwards, the only issue may be that events don't fire as expected.
Yeah.
If it really makes sense to go backwards but you don't want to modify or make a new AnimationState.cs but you still want to go programmatic and generally leave it to AnimationState, you may have to temporarily take control of the TrackEntry away from AnimationState like this:
// keep your backwards-playing TrackEntry in a variable (myTrackEntry).
void Update () {
if (goingBackwards) {
myTrackEntry.timeScale = 0f; // so time isn't moved forward by SkeletonAnimation/AnimationState
myTrackEntry.lastTime = 0f; // this may cause multiple events to fire if you have those.
myTrackEntry.time = targetTime; // you should decrement targetTime by a delta time (to act as the animation time cursor that moves backwards.)
skeletonAnimation.state.Apply(skeletonAnimation.skeleton); // may be required, depending on project script execution order settings. It would be more efficient if this script runs before the SkeletonAnimation.cs and you won't need this.
}
}
// and when you're done going backwards, set myTrackEntry.timeScale back to 1;
Clever, Pharan.
For anyone coming here and getting confused, here's a solution that might work!
The lastTime and time parameters have been renamed since this post was made. By looking at the docs, I figured these parameters would be correct, and I think I figured it out!
Here's what I tried:
// targetTime is a float, defined in the class
animationBackwards = true; // I have this enabled just to force it to play backwards while debugging.
if (animationBackwards) {
currentMovementTrack.timeScale = 0f; // so time isn't moved forward by SkeletonAnimation/AnimationState
currentMovementTrack.animationLast = 0f; // this may cause multiple events to fire if you have those.
currentMovementTrack.trackTime = targetTime; // you should decrement targetTime by a delta time (to act as the animation time cursor that moves backwards.)
skeletonAnim.state.Apply(skeletonAnim.skeleton); // may be required, depending on project script execution order settings. It would be more efficient if this script runs before the SkeletonAnimation.cs and you won't need this.
targetTime -= Time.deltaTime;
if (targetTime <= 0) targetTime = currentMovementTrack.animationEnd; // You need to make sure the trackTime is within the limits of the animation, or will not work
} else {
currentMovementTrack.timeScale = 1f;
}
I still need to figure out how to change the speed, since I do that with currentMovementTrack.timeScale normally, but I guess I would multiple the animation speed with Time.deltaTime.
The use case for this is my character can aim a gun to the right, and walk to the left, and the animation should be playing backwards.
Like others have said, it's not practical to duplicate all animations to go in two directions, since any adjustments to the orignal animation would require a lot of work.
I can see that it might not be possible to play an animation backwards and maintain all features, but it's a bit unwieldy to have to do it this way, and it would be practical if it was supported by the API.
I would probably like to have events on footsteps down, but didn't try that yet, and multiple events firing when playing backwards doesn't seem very good, but we'll see.
Hope this helps anyone coming here
Sorry to revive this thread but i'd like to ask about the reasoning for the fact that AnimationState doesn't support negative timescales.
I presume there's something fundamental to the spine architecture that has led to this decision, but i have to say i've found myself wanting to set negative timescales many many times and I'm onstantly frustrated that it's not supported. The workaround is either a bolted-on piece of code that requires a bunch of special case testing or adding to my animator's workload significantly. It seems that it's not straightforward for my animator to make reversed clips with a touch of a button, the impression that i get is it's quite fiddly to do well.
Given that this has such obvious use-cases is there any chance that negative timescale support could be implemented in future?
AnimationState assumes animations are going forward. It uses the animation time to make decisions about when to play the next queued animation on that track and the timing of mixing between animations on the same track. If the animation time were to go backward, these things would not work correctly.
To support AnimationState playing backward, the easiest thing would be to flip the animation time when applying timelines, but leave it moving forward everywhere else. For example:
float time = ...;
if (backward) time = current.animation.getDuration() - time;
for (int i = 0; i < timelineCount; i++)
timelines[i].apply(skeleton, 0, time, null, mix, blend, MixDirection.in);
We'll consider this for 3.9.
Most timelines don't care what time they are applied, but EventTimeline does. It uses the lastTime
and time
parameters to know what events to fire: everything between lastTime
(exclusive) and time
(inclusive). When playing backward, events won't fire. EventTimeline has no way to know that it is playing backward. Adding yet another parameter would be unfortunate.
Support for AnimationState to play backward has been added to 3.9-beta:
Added support for AnimationState to play backward.@be8f622
The only caveat is that events are not fired.
Thanks for the response. I figured it was deep in there. There's clearly quite a lot going on in AnimationState that relies on the assumption for forwards playing timelines. Im guessing this was a decision made early on and the issue now is that changing it to be "play direction agnostic" would require a full re-write of a lot of the core code in that class? Anyway I appreciate the clarity and communication. I've manage to get the hacky version to work so it's not blocking me too much.
Spiral Circus wrotethe issue now is that changing it to be "play direction agnostic" would require a full re-write of a lot of the core code in that class?
No issue, it has been added in 3.9, see the commit linked above! Note the commit is in the 3.9-beta branch, but could be back ported to 3.8 pretty easily. We won't do so though because 3.8 has been released and the priority for it is to be stable. To use it:
TrackEntry entry = state.addAnimation(0, "run", true, 0);
entry.reverse = true;