-
Notifications
You must be signed in to change notification settings - Fork 16
/
ReverseAnimationContext.cs
136 lines (111 loc) · 4.73 KB
/
ReverseAnimationContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Animations;
public static class ReverseAnimationContext
{
[MenuItem("Assets/Create Reversed Clip", false, 14)]
private static void ReverseClips()
{
var animators = Object.FindObjectsByType<Animator>(FindObjectsSortMode.None);
AssetDatabase.FindAssets("t:AnimatorController");
List<AnimationClip> clips = GetSelectedClips();
if (clips is not { Count: > 0 })
return;
foreach (AnimationClip clip in clips)
{
ReverseClip(clip, animators);
}
Debug.Log("All selected clips reversed");
}
private static List<AnimationClip> GetSelectedClips()
{
var clips = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets);
if (clips.Length <= 0)
return null;
return clips.Select(clip => clip as AnimationClip).ToList();
}
private static void ReverseClip(AnimationClip clip, Animator[] animators)
{
AnimationClip originalClip = clip;
string directoryPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(clip));
string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(clip));
string fileExtension = Path.GetExtension(AssetDatabase.GetAssetPath(clip));
fileName = fileName.Split('.')[0];
string copiedFilePath = directoryPath + Path.DirectorySeparatorChar + fileName + "_Reversed" + fileExtension;
AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(clip), copiedFilePath);
clip = (AnimationClip)AssetDatabase.LoadAssetAtPath(copiedFilePath, typeof(AnimationClip));
if (clip == null)
return;
float clipLength = clip.length;
var curves = AnimationUtility.GetCurveBindings(clip);
foreach (EditorCurveBinding binding in curves)
{
var animCurve = AnimationUtility.GetEditorCurve(clip, binding);
var keys = animCurve.keys;
int keyCount = keys.Length;
for (int i = 0; i < keyCount; i++)
{
Keyframe kf = keys[i];
kf.time = clipLength - kf.time;
var tmp = -kf.inTangent;
kf.inTangent = -kf.outTangent;
kf.outTangent = tmp;
keys[i] = kf;
}
animCurve.keys = keys;
clip.SetCurve(binding.path, binding.type, binding.propertyName, animCurve);
}
var events = AnimationUtility.GetAnimationEvents(clip);
if (events.Length > 0)
{
foreach (var e in events)
{
e.time = clipLength - e.time;
}
AnimationUtility.SetAnimationEvents(clip, events);
}
var objectReferenceCurves = AnimationUtility.GetObjectReferenceCurveBindings(clip);
foreach (EditorCurveBinding binding in objectReferenceCurves)
{
ObjectReferenceKeyframe[] objectReferenceKeyframes =
AnimationUtility.GetObjectReferenceCurve(clip, binding);
for (int i = 0; i < objectReferenceKeyframes.Length; i++)
{
ObjectReferenceKeyframe kf = objectReferenceKeyframes[i];
//K.time = clipLength - K.time - (1 / clip.frameRate); //Reversed sprite clips may be offset by 1 frame time
kf.time = clipLength - kf.time;
objectReferenceKeyframes[i] = kf;
}
AnimationUtility.SetObjectReferenceCurve(clip, binding, objectReferenceKeyframes);
}
foreach (Animator anim in animators)
{
AnimationClip[] clips = AnimationUtility.GetAnimationClips(anim.gameObject);
if (clips.All(c => c != originalClip))
continue;
Debug.Log("Found the animator containing the original clip that was reversed, adding new clip to its state machine...");
AnimatorController controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GetAssetPath(anim.runtimeAnimatorController));
AnimatorStateMachine asm = controller.layers[0].stateMachine;
AnimatorState animState = asm.AddState(clip.name);
animState.motion = clip;
break;
}
}
[MenuItem("Assets/Create Reversed Clip", true)]
private static bool ReverseClipValidation()
{
return Selection.activeObject && Selection.activeObject is AnimationClip;
}
public static AnimationClip GetSelectedClip()
{
var clips = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets);
if (clips.Length > 0)
{
return clips[0] as AnimationClip;
}
return null;
}
}