Skip to content

Instantly share code, notes, and snippets.

@8chan-co
Last active July 29, 2024 21:23
Show Gist options
  • Save 8chan-co/b288d25ab92ba9981db3bb01ad69a3b5 to your computer and use it in GitHub Desktop.
Save 8chan-co/b288d25ab92ba9981db3bb01ad69a3b5 to your computer and use it in GitHub Desktop.
/*
NOTE: to use this Unity Editor utility, make a folder (Create > Folder) inside of Unity, and name it "Editor",
in that folder, make a C# script (Create > C# Script) and preferably name it (MirrorAnimationEditor),
open the C# script file in a File Editor (such as Note Pad) to paste the below C# code inside of it,
save the file, then head back to Unity; wait a sec or 2 until Unity finishes compiling the script,
once Unity is done compiling; you should be able to see a Button called "Mirror Animation" on the
Inspector Window when you select an Animation Clip in the Project Window, clicking the button will make a
new Animation Clip with the same name and path as the one inspected suffixed with "Mirrored", this newly
created Animation Clip will reflect all properties of the original, with the exception where humanoid rig
properties that animate a given side of the avatar will now oppose that side to mirror the Animation Clip.
*/
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[CustomEditor(typeof(AnimationClip))]
internal sealed class MirroredAnimationVariantEditor : Editor
{
private static readonly System.Type s_NativeEditorType =
typeof(Editor).Assembly.GetType("UnityEditor.AnimationClipEditor");
private EditorCurveBinding[] m_Bindings;
private AnimationCurve[] m_Curves;
private Editor m_NativeEditor;
private Button m_CreationButton;
private void OnEnable()
{
var animation = target as AnimationClip;
m_NativeEditor = CreateEditor(animation, s_NativeEditorType);
m_CreationButton = new(CreateMirroredVariant)
{
text = ObjectNames.NicifyVariableName(nameof(CreateMirroredVariant))
};
m_Bindings = AnimationUtility.GetCurveBindings(animation);
m_Curves = System.Array.ConvertAll(AnimationUtility.GetCurveBindings(animation),
binding => AnimationUtility.GetEditorCurve(animation, binding)
);
const string Left = nameof(Left), Right = nameof(Right);
for (var i = 0; i < m_Bindings.Length; ++i)
{
if (m_Bindings[i].type != typeof(Animator)) continue;
if (m_Bindings[i].propertyName.StartsWith(Left))
m_Bindings[i].propertyName = m_Bindings[i].propertyName.Replace(Left, Right);
else if (m_Bindings[i].propertyName.StartsWith(Right))
m_Bindings[i].propertyName = m_Bindings[i].propertyName.Replace(Right, Left);
}
m_CreationButton.visible = m_Bindings.Any(binding =>
binding.propertyName.StartsWith(Left) | binding.propertyName.StartsWith(Right)
);
if (m_CreationButton.visible is false) return;
var width = System.MathF.Min(
m_CreationButton.MeasureTextSize(m_CreationButton.text,
float.NaN, VisualElement.MeasureMode.Undefined,
float.NaN, VisualElement.MeasureMode.Undefined
).x, EditorWindow.focusedWindow.position.width
);
m_CreationButton.style.width = System.MathF.Round(width += width * 0.25F);
m_CreationButton.style.alignSelf = Align.Center;
}
public override VisualElement CreateInspectorGUI()
{
VisualElement inspector = new();
inspector.Add(new IMGUIContainer(m_NativeEditor.OnInspectorGUI));
inspector.Add(m_CreationButton);
return inspector;
}
private void OnDestroy() => DestroyImmediate(m_NativeEditor);
private void CreateMirroredVariant()
{
AnimationClip animation = new();
AnimationUtility.SetEditorCurves(animation, m_Bindings, m_Curves);
var pathname = AssetDatabase.GetAssetPath(target);
var destination = Path.Join(Path.GetDirectoryName(pathname),
string.Concat(Path.GetFileNameWithoutExtension(pathname),
"Mirrored", Path.GetExtension(pathname)
)
);
AssetDatabase.CreateAsset(animation, AssetDatabase.GenerateUniqueAssetPath(destination));
Selection.SetActiveObjectWithContext(animation, EditorWindow.focusedWindow);
EditorGUIUtility.PingObject(animation);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment