@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.’
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):
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
using UnityEditor.Animations;
public static class ReverseAnimationContext
{
[MenuItem("Assets/Create Reversed Clip", false, 14)]
private static void ReverseClips()
{
List<AnimatorController> animConts = new List<AnimatorController>();
var animators = Object.FindObjectsOfType<Animator>();
AssetDatabase.FindAssets("t:AnimatorController");
List<AnimationClip> clips = GetSelectedClips();
if (clips != null && clips.Count > 0)
{
foreach (AnimationClip clip in clips)
{
ReverseClip(clip, animators);
This file has been truncated. show original
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!
8 Likes
Thank you @Straafe ! Get it while it’s fresh, @Elin42 @rudehouse , @pinaeong , @thierry_unity , @vladk ,
ReverseAnimationContext.cs allows you to easily reverse animation clips:
1 Like
Thank you @Straafe , this is really amazing, unity should integrate this feature by default. You saved us a lot of time.
1 Like
@Straafe you are amazing thank you so much. You saved my day
1 Like
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
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.
Straafe:
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.
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.
Vivraan
September 15, 2020, 6:23pm
29
Straafe:
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.
using UnityEditor;
using UnityEngine;
using System.IO;
public static class ReverseAnimationContext
{
[MenuItem("Assets/Create Reversed Clip", false, 14)]
private static void ReverseClip()
{
string directoryPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(Selection.activeObject));
string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject));
string fileExtension = Path.GetExtension(AssetDatabase.GetAssetPath(Selection.activeObject));
fileName = fileName.Split('.')[0];
string copiedFilePath = directoryPath + Path.DirectorySeparatorChar + fileName + "_Reversed" + fileExtension;
var clip = GetSelectedClip();
AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(Selection.activeObject), copiedFilePath);
clip = (AnimationClip)AssetDatabase.LoadAssetAtPath(copiedFilePath, typeof(AnimationClip));
if (clip == null)
return;
float clipLength = clip.length;
var curves = AnimationUtility.GetAllCurves(clip, true);
clip.ClearCurves();
foreach (AnimationClipCurveData curve in curves)
{
var keys = curve.curve.keys;
int keyCount = keys.Length;
var postWrapmode = curve.curve.postWrapMode;
curve.curve.postWrapMode = curve.curve.preWrapMode;
curve.curve.preWrapMode = postWrapmode;
for (int i = 0; i < keyCount; i++)
{
Keyframe K = keys[i];
K.time = clipLength - K.time;
var tmp = -K.inTangent;
K.inTangent = -K.outTangent;
K.outTangent = tmp;
keys[i] = K;
}
curve.curve.keys = keys;
clip.SetCurve(curve.path, curve.type, curve.propertyName, curve.curve);
}
var events = AnimationUtility.GetAnimationEvents(clip);
if (events.Length > 0)
{
for (int i = 0; i < events.Length; i++)
{
events[i].time = clipLength - events[i].time;
}
AnimationUtility.SetAnimationEvents(clip, events);
}
Debug.Log("Animation reversed!");
}
[MenuItem("Assets/Create Reversed Clip", true)]
static bool ReverseClipValidation()
{
return Selection.activeObject.GetType() == typeof(AnimationClip);
}
public static AnimationClip GetSelectedClip()
{
var clips = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets);
if (clips.Length > 0)
{
return clips[0] as AnimationClip;
}
return null;
}
}
@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.
Straafe
September 15, 2020, 7:07pm
30
@Vivraan Hey, that sounds perfectly fine to me. I believe I updated it to not use the deprecated functions here: unity-editor-tools/ReverseAnimationContext.cs at master · Straafe/unity-editor-tools · GitHub
Vivraan
September 15, 2020, 7:12pm
31
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.)
1 Like
Vivraan
September 15, 2020, 7:17pm
32
Really like the multiple clips reversal context, although I believe just reversing one at a time would suffice! (We seriously need our animators to make reversed versions since this isn’t Godot :p)
Vivraan
September 15, 2020, 7:27pm
33
The corrected version is:
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Linq;
namespace Helpers
{
/// Originally from Straafe
/// From https://discussions.unity.com/t/678635/10
public static class ReverseAnimationContext
{
[MenuItem("Assets/Create Reversed Clip", false, 14)]
private static void ReverseClip()
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
string directoryPath = Path.GetDirectoryName(path);
string fileName = Path.GetFileName(path).Split('.')[0];
string fileExtension = Path.GetExtension(path);
string copiedFilePath = Path.Combine(directoryPath, $"{fileName}_Reversed{fileExtension}");
AssetDatabase.CopyAsset(path, copiedFilePath);
var clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(copiedFilePath);
if (clip == null)
{
return;
}
float clipLength = clip.length;
var editorBindings = AnimationUtility.GetCurveBindings(clip);
foreach (var binding in editorBindings)
{
var curve = AnimationUtility.GetEditorCurve(clip, binding);
var keys = curve.keys;
var postWrapmode = curve.postWrapMode;
curve.postWrapMode = curve.preWrapMode;
curve.preWrapMode = postWrapmode;
for (int i = 0; i < keys.Length; i++)
{
var K = keys[i];
K.time = clipLength - K.time;
var tmp = -K.inTangent;
K.inTangent = -K.outTangent;
K.outTangent = tmp;
keys[i] = K;
}
curve.keys = keys;
clip.SetCurve(binding.path, binding.type, binding.propertyName, curve);
}
var events = AnimationUtility.GetAnimationEvents(clip);
foreach (var @event in events)
{
@event.time = clipLength - @event.time;
}
AnimationUtility.SetAnimationEvents(clip, events);
Debug.Log("Animation reversed!");
}
[MenuItem("Assets/Create Reversed Clip", true)]
private static bool ReverseClipValidation() => Selection.activeObject is AnimationClip;
private static AnimationClip SelectedClip => Selection.GetFiltered<AnimationClip>(SelectionMode.Assets).FirstOrDefault();
}
}
5 Likes
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!!!
8 Likes
I agree, negative speed has worked for some time now??
SiWoC
March 13, 2021, 12:25pm
36
animator.speed = -1f;
gives me
Animator.speed can only be negative when Animator recorder is enabled. Animator.recorderMode != AnimatorRecorderMode.Offline
Are you setting the speed on the state or the whole animator?
SiWoC
March 21, 2021, 12:56pm
38
Of the animator (GetComponent())
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?
SiWoC:
Of the animator (GetComponent())
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?
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.
Thanks so much for the script I would have sat there for days without it