Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

How can I play an animation backwards?

Discussion in 'Timeline' started by mrpmorris, Oct 1, 2017.

  1. mrpmorris

    mrpmorris

    Joined:
    Dec 25, 2012
    Posts:
    50
    I've added an animation to my timeline, but the timescale on that animation won't let me specify a negative number. How can I play the animation in reverse? (Speed -1).
     
  2. thierry_unity

    thierry_unity

    Unity Technologies

    Joined:
    Jun 10, 2015
    Posts:
    184
    Hi,
    Unfortunately, right now there is no way to play an animation in reverse inside timeline. It's something that came up a couple of time but there is no easy way to do it. Only solution I can give you is to duplicate your clip and inside the animation window, using the box tool, you could reverse the 2nd clip.
     
    DenisK_WRG, KQQky and TheWarper like this.
  3. mrpmorris

    mrpmorris

    Joined:
    Dec 25, 2012
    Posts:
    50
    Ah damn, that's really disappointing!

    Thanks for your answer.
     
  4. vladk

    vladk

    Joined:
    Jul 10, 2008
    Posts:
    166
    could you please explain how to do it? What's a box tool?
     
  5. Sergey_Droba

    Sergey_Droba

    Joined:
    Nov 6, 2016
    Posts:
    50
  6. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    355
    Timeline can't currently play animations backwards.

    To fix this, you can duplicate your animation in the project window, and then create a reversed copy in "Keys" view by dragging right to its end then scaling negatively, which works, but is a pain on large animations as the Animation UI becomes quite unresponsive.

    Or, you can clone your anim and run this script:

    https://answers.unity.com/questions/476819/reverse-animation-help.html

    Both confirmed working in 2017.4, but the script version is much less hassle.

    It would be nice to be able to create the reversed animation clip at runtime, but I believe that's not possible until 2018.2 (?)
     
    michaelday008, fbittner and TheWarper like this.
  7. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
    Is this now possible?
     
  8. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
  9. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
  10. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
    If it helps anyone else, I modified the script above to be inside the context menu when you right click on an animation clip. It also automatically creates the copy and adds "_Reversed" to the name so you don't have to copy it first. Not perfect, but it saves me some time.



    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using System.IO;
    4.  
    5. public static class ReverseAnimationContext
    6. {
    7.  
    8.     [MenuItem("Assets/Create Reversed Clip", false, 14)]
    9.     private static void ReverseClip()
    10.     {
    11.         string directoryPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(Selection.activeObject));
    12.         string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject));
    13.         string fileExtension = Path.GetExtension(AssetDatabase.GetAssetPath(Selection.activeObject));
    14.         fileName = fileName.Split('.')[0];
    15.         string copiedFilePath = directoryPath + Path.DirectorySeparatorChar + fileName + "_Reversed" + fileExtension;
    16.         var clip = GetSelectedClip();
    17.  
    18.         AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(Selection.activeObject), copiedFilePath);
    19.  
    20.         clip  = (AnimationClip)AssetDatabase.LoadAssetAtPath(copiedFilePath, typeof(AnimationClip));
    21.  
    22.         if (clip == null)
    23.             return;
    24.         float clipLength = clip.length;
    25.         var curves = AnimationUtility.GetAllCurves(clip, true);
    26.         clip.ClearCurves();
    27.         foreach (AnimationClipCurveData curve in curves)
    28.         {
    29.             var keys = curve.curve.keys;
    30.             int keyCount = keys.Length;
    31.             var postWrapmode = curve.curve.postWrapMode;
    32.             curve.curve.postWrapMode = curve.curve.preWrapMode;
    33.             curve.curve.preWrapMode = postWrapmode;
    34.             for (int i = 0; i < keyCount; i++)
    35.             {
    36.                 Keyframe K = keys[i];
    37.                 K.time = clipLength - K.time;
    38.                 var tmp = -K.inTangent;
    39.                 K.inTangent = -K.outTangent;
    40.                 K.outTangent = tmp;
    41.                 keys[i] = K;
    42.             }
    43.             curve.curve.keys = keys;
    44.             clip.SetCurve(curve.path, curve.type, curve.propertyName, curve.curve);
    45.         }
    46.         var events = AnimationUtility.GetAnimationEvents(clip);
    47.         if (events.Length > 0)
    48.         {
    49.             for (int i = 0; i < events.Length; i++)
    50.             {
    51.                 events[i].time = clipLength - events[i].time;
    52.             }
    53.             AnimationUtility.SetAnimationEvents(clip, events);
    54.         }
    55.         Debug.Log("Animation reversed!");
    56.     }
    57.  
    58.     [MenuItem("Assets/Create Reversed Clip", true)]
    59.     static bool ReverseClipValidation()
    60.     {
    61.         return Selection.activeObject.GetType() == typeof(AnimationClip);
    62.     }
    63.  
    64.     public static AnimationClip GetSelectedClip()
    65.     {
    66.         var clips = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets);
    67.         if (clips.Length > 0)
    68.         {
    69.             return clips[0] as AnimationClip;
    70.         }
    71.         return null;
    72.     }
    73.  
    74. }
    75.  
     
  11. xhdfgT

    xhdfgT

    Joined:
    Feb 12, 2017
    Posts:
    1
    Your Script is Amazing, it is working fine, Thanks for sharing
     
    Straafe likes this.
  12. Elin42

    Elin42

    Joined:
    Oct 21, 2014
    Posts:
    22
    Such a useful function. This should be in unity by default, if there is no easy way to reverse a clip in Timeline.
     
  13. Mr_Jigs

    Mr_Jigs

    Joined:
    Apr 18, 2015
    Posts:
    69
    Straafe likes this.
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    1,522
    @Straafe You could change the menu path to "CONTEXT/AnimationClip/Create Reversed Clip" to make it a context menu function on the AnimationClip asset instead. Give it a "MenuCommand command" parameter and use "command.context" to access the clip it's being called on instead of "Selection.activeObject". That way you could select multiple clips and run the function for all of them at once (and it isn't taking up space in the main Assets menu).
     
    fbittner and ferretnt like this.
  15. pinaeong

    pinaeong

    Joined:
    Dec 17, 2019
    Posts:
    1
    @Straafe You are so amazing!! Thank you so much. Can I post your code on my blog? Of course, I'll write where it came from and who wrote it. Thank you!
     
  16. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
  17. steveh2112

    steveh2112

    Joined:
    Aug 30, 2015
    Posts:
    246
    sorry but where exactly do i place this code? thanks
     
  18. dasparadoxon

    dasparadoxon

    Joined:
    Aug 16, 2014
    Posts:
    5
    Thank you @Straafe for this script !!!!

    (@steveh2112 : put it into an folder named Editor, also make sure that the file is named ReverseAnimationContext.cs )
     
    Straafe likes this.
  19. rudehouse

    rudehouse

    Joined:
    Feb 16, 2019
    Posts:
    4


    In this video the man change the speed to negative and the animation starts playing backwards
     
  20. eldvbear

    eldvbear

    Joined:
    Jul 21, 2016
    Posts:
    7
     
    seant_unity likes this.
  21. BellBlitzKing

    BellBlitzKing

    Joined:
    Feb 9, 2018
    Posts:
    10
    @Straafe and @Bunny83
    Unity 2019 has updated a few things which changed what this generates. Can you provide an update?

    Unity gives me the warning:
    Assets\Editor\ReverseAnimationContext.cs(25,22): warning CS0618: 'AnimationUtility.GetAllCurves(AnimationClip, bool)' is obsolete: 'GetAllCurves is deprecated. Use GetCurveBindings and GetObjectReferenceCurveBindings instead.'
     
  22. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
    Hi @BellBlitzKing ,

    I updated the script above to use the newer functions and no longer use the deprecated one. It also now supports multiple animation clips at a time, so you can shift-click or ctrl-click multiple clips and reverse them all at once.

    You can find it here (haven't done much testing, so let me know if there's a problem):
    https://github.com/Straafe/unity-editor-tools/blob/master/ReverseAnimationContext.cs

    I also added a couple more scripts there, one to add a default empty state to an AnimationController and one to unloop AnimationClips which default to looped most of the time. Both of those also support multi select and have helped me a lot in some situations. Still not perfect, but it works.

    https://github.com/Straafe/unity-editor-tools

    @Kybernetik I got it to work with multiple clips at once, but please improve it!
     
    Last edited: Apr 27, 2020
  23. BellBlitzKing

    BellBlitzKing

    Joined:
    Feb 9, 2018
    Posts:
    10
    Straafe likes this.
  24. pikachuxxx

    pikachuxxx

    Joined:
    Jan 22, 2019
    Posts:
    17
    Thank you @Straafe, this is really amazing, unity should integrate this feature by default. You saved us a lot of time.
     
    Straafe likes this.
  25. berk_can

    berk_can

    Joined:
    Feb 8, 2016
    Posts:
    12
    @Straafe you are amazing thank you so much. You saved my day
     
    Straafe likes this.
  26. Djaydino

    Djaydino

    Joined:
    Aug 19, 2012
    Posts:
    44
    Hi.
    At the moment it does not seem to work with sprites, but it still helped me with my animated colliders etc.

    would it be possible to have this work for sprites as well?

    Anyway still Thanks @Straafe as it saved me hand setting collider animations :)
     
  27. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
    Hi @Djaydino , I almost exclusively work with 3D models and 3D scenes, so I haven't done much in 2D with sprite or sprite animations. Do they still use Animation Clips? In theory it should work with anything that uses clips.
     
  28. Djaydino

    Djaydino

    Joined:
    Aug 19, 2012
    Posts:
    44
    Hi.
    Thank for your reply.

    Yes they are within the clip and on a older version i copied from here they where disappeared after reversed.
    Then i tried with the github version and with this version they do not disappear, but they are also not reversing.

    But i think it is probably not possible.
    If you select a clip directly from project then you are not able to add sprites, only move/remove.
     
  29. Vivraan

    Vivraan

    Joined:
    Feb 2, 2018
    Posts:
    13
    @Straafe Please tell me how I should attribute you since I want to use this in production. At the moment, I want to provide your user handle and the link to this post on the top of the code.

    My only complaint is that `AnimationUtility.GetAllCurves` is deprecated now.
     
  30. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    67
  31. Vivraan

    Vivraan

    Joined:
    Feb 2, 2018
    Posts:
    13
    Alright, so I modified the script to be a bit more "modern" (if at the lack of compatibility) and replaced the
    GetAllCurves
    method with the editor bindings API.

    @Straafe could you comment on the shortcomings of this edit I made?

    (See below for updated script.)
     
    Last edited: Sep 28, 2020
    BellBlitzKing likes this.
  32. Vivraan

    Vivraan

    Joined:
    Feb 2, 2018
    Posts:
    13
  33. Vivraan

    Vivraan

    Joined:
    Feb 2, 2018
    Posts:
    13
    The corrected version is:


    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using System.IO;
    4. using System.Linq;
    5.  
    6. namespace Helpers
    7. {
    8.     /// Originally from Straafe
    9.     /// From https://forum.unity.com/threads/how-can-i-play-an-animation-backwards.498287/#post-5004851
    10.     public static class ReverseAnimationContext
    11.     {
    12.         [MenuItem("Assets/Create Reversed Clip", false, 14)]
    13.         private static void ReverseClip()
    14.         {
    15.             string path = AssetDatabase.GetAssetPath(Selection.activeObject);
    16.             string directoryPath = Path.GetDirectoryName(path);
    17.             string fileName = Path.GetFileName(path).Split('.')[0];
    18.             string fileExtension = Path.GetExtension(path);
    19.             string copiedFilePath = Path.Combine(directoryPath, $"{fileName}_Reversed{fileExtension}");
    20.             AssetDatabase.CopyAsset(path, copiedFilePath);
    21.  
    22.             var clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(copiedFilePath);
    23.  
    24.             if (clip == null)
    25.             {
    26.                 return;
    27.             }
    28.  
    29.             float clipLength = clip.length;
    30.             var editorBindings = AnimationUtility.GetCurveBindings(clip);
    31.  
    32.             foreach (var binding in editorBindings)
    33.             {
    34.                 var curve = AnimationUtility.GetEditorCurve(clip, binding);
    35.                 var keys = curve.keys;
    36.  
    37.                 var postWrapmode = curve.postWrapMode;
    38.                 curve.postWrapMode = curve.preWrapMode;
    39.                 curve.preWrapMode = postWrapmode;
    40.  
    41.                 for (int i = 0; i < keys.Length; i++)
    42.                 {
    43.                     var K = keys[i];
    44.                     K.time = clipLength - K.time;
    45.  
    46.                     var tmp = -K.inTangent;
    47.                     K.inTangent = -K.outTangent;
    48.                     K.outTangent = tmp;
    49.  
    50.                     keys[i] = K;
    51.                 }
    52.  
    53.                 curve.keys = keys;
    54.                 clip.SetCurve(binding.path, binding.type, binding.propertyName, curve);
    55.             }
    56.  
    57.             var events = AnimationUtility.GetAnimationEvents(clip);
    58.             foreach (var @event in events)
    59.             {
    60.                 @event.time = clipLength - @event.time;
    61.             }
    62.             AnimationUtility.SetAnimationEvents(clip, events);
    63.  
    64.             Debug.Log("Animation reversed!");
    65.         }
    66.  
    67.         [MenuItem("Assets/Create Reversed Clip", true)]
    68.         private static bool ReverseClipValidation() => Selection.activeObject is AnimationClip;
    69.  
    70.         private static AnimationClip SelectedClip => Selection.GetFiltered<AnimationClip>(SelectionMode.Assets).FirstOrDefault();
    71.     }
    72. }
    73.  
     
    Straafe and BellBlitzKing like this.
  34. ThirtysixLab

    ThirtysixLab

    Joined:
    Dec 6, 2013
    Posts:
    4
    Hi everyone!

    in Unity (until 2020) I just typed:
    animator.speed = -1f;

    in Unity 2020 (and above) I need to enter:
    animator.StartPlayback ();
    animator.speed = -1f;

    It works for me!!!
     
    MousePods, donkey0t, Gorlog and 3 others like this.
  35. donkey0t

    donkey0t

    Joined:
    Oct 23, 2016
    Posts:
    57
    I agree, negative speed has worked for some time now??
     
  36. SiWoC

    SiWoC

    Joined:
    Feb 16, 2021
    Posts:
    5
    animator.speed = -1f;

    gives me

    Animator.speed can only be negative when Animator recorder is enabled. Animator.recorderMode != AnimatorRecorderMode.Offline
     
  37. donkey0t

    donkey0t

    Joined:
    Oct 23, 2016
    Posts:
    57
    Are you setting the speed on the state or the whole animator?
     
  38. SiWoC

    SiWoC

    Joined:
    Feb 16, 2021
    Posts:
    5
    Of the animator (GetComponent<Animator>())

    Property or indexer 'AnimatorStateInfo.speed' cannot be assigned to -- it is read only

    Why are you suggesting this?
    Do you know another way through "state"? Which state do you mean?
    Do you have code showing this?
     
  39. donkey0t

    donkey0t

    Joined:
    Oct 23, 2016
    Posts:
    57
    In the animator statemachine, select the state and in the inspector set the speed to -ve. I.e. each state plays its animation at a specific speed.
     
  40. SammyHD

    SammyHD

    Joined:
    Apr 26, 2020
    Posts:
    1
    Thanks so much for the script I would have sat there for days without it
     
unityunity