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.

Related Discussions
...

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. ๐Ÿ™‚

3 years later

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 ๐Ÿ™‚

5 months later

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 wrote

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?

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;

Oh sorry i guess i misunderstood. I'll talk to my animator about 3.9. Thanks again.

Sure, no problem! Note that 3.9 hasn't been released yet, even as beta. Your animator is going to love it. ๐Ÿ™‚

5 months later

Just encountered this... because I want events to trigger, this form of reversing wouldn't work. Also, grabbing all the keys in the editor and dragging them in reverse order doesn't seem to work for me - maybe because ik or constraints don't work the same when you reverse the keys? My character's feet are going through the floor and the animated image regions are twitching at the loop point. It's hard to tell what exactly is off. Edit: It's at least partially becasue if you keyed the "mix" parameter on the first frame, this is now on the last frame, so the ik and constraints only apply for 1 frame.

I was thinking you could sidestep this if there was an option "bake" animations, or at least "bake" them in reverse - basically have it do whatever the "play backward" button does in the editor and key every frame (but also including events in this). So at least it would be easier to make a reverse animation in the editor.

Another idea I had was if it could have an option to export anims in reverse, with "_Reverse" added onto the end.