Search Unity

[Free] Mirror/copy animation curves

Discussion in 'Scripting' started by Sundersoft, Sep 15, 2012.

  1. Sundersoft

    Sundersoft

    Joined:
    Sep 15, 2012
    Posts:
    4
    Hi,

    This script will allow you to copy animation curves from one object in an animation clip to another, or make a mirrored object animate the same as a non-mirrored one. The full documentation is in the form of a comment at the top of the script.

    This is useful if you're making your animations in Unity.

    To install it, make a file called "mirror_animation_curves.cs" in the "Assets/Editor" folder of your project. The documentation will tell you how to actually use it.

    Tell me if you need any help with the script or find any bugs.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections.Generic;
    5.  
    6. /*
    7. This script will find all of the animation curves under "From root", possibly
    8. negate them, and copy the resultant animation curves to "To root".
    9.  
    10. It will operate on the default animation clip of the currently selected
    11. object. This may be different from the clip you have selected in the
    12. animation editor, so be sure to change the "Animation" setting of the
    13. animation you want to operate on in the Inspector.
    14.  
    15. To then use the script, select "Mirror Animation Curves" in the "Window"
    16. menu of Unity and fill in the options and click one of the buttons to
    17. activate the script.
    18.  
    19. When naming the new curves, it will remove the "From prefix" from the start
    20. of the original curve's name if it is present, and replace it with the the
    21. "To prefix". It will also remove the "From appendage" from the end of the
    22. original curve's name if present and append the "To appendage".
    23.  
    24. For example, if you had an AnimationClip which animated the Right_arm in the
    25. Body child of whatever it was attached to, and you wanted to mirror the curves
    26. so that they applied to the Left_arm, your original curves would be named like
    27. this (using the hand as an example):
    28.  
    29. Body/Right_arm/Right_forearm/Right_hand
    30.  
    31. And your new curves would be named like this:
    32.  
    33. Body/Left_arm/Left_forearm/Left_hand
    34.  
    35. In this case, you would set the "From root" to "Body/Left_arm" and the
    36. "To root" to "Body/Right_arm" because you want to mirror the entire left
    37. arm and all of its children and write the curves to the right arm.
    38. The "From prefix" should be set to "Right" and the "To prefix" should be set to
    39. "Left" because you want to change "Right" to "Left" at the start of all of the
    40. names. Note that the root names are not changed because you already set the new
    41. root name. The appendages (From appendage and To appendage) should be set to
    42. nothing because you don't want to change anything at the end of the names.
    43.  
    44. The script will then use these rules to change "Right_forearm" to "Left_forearm"
    45. and "Right_hand" to "Left_hand" when it is copying the animation curves
    46. for the hand.
    47.  
    48. Also, you may want to copy animation curves from one object to a mirror image
    49. of that object. In this case, you have to set up your mirrored object so that
    50. two axis preserve their original meaning and one axis is inverted. For example,
    51. your right thumb may be oriented so that the X axis points towards the arm,
    52. the Y axis points towards the index finger, and the Z axis points
    53. towards the palm. When you mirror it, you have to change the meaning of
    54. one of these axis because you have to end up with a left-handed coordinate
    55. system. For example, you might have the mirrored thumb's X axis continue to
    56. point towards the mirrored arm and the Y axis continue to point
    57. towards the mirrored index finger, but the Z axis would now point away from the
    58. palm because the coordinate system has to be left handed. In this case you
    59. would use the "Mirror Z" setting. Note that your mirrored armature has
    60. to have only the Z axis inverted like this for every bone you are mirroring
    61. or the script won't work (i.e. you have to be consistent).
    62.  
    63. If you have a different animation for your mirrored object, you can still use
    64. this script. First make a copy of the animation clip and change the mirrored
    65. object to use the copy, then run this script on the mirrored object with the
    66. from root and to root both set to nothing. If your children are named the
    67. same in each object, you can also set the prefixes and appendages to nothing,
    68. otherwise you will be asked to clean up animation curves which you may do.
    69. */
    70.  
    71. public class mirror_animation_curves : EditorWindow {
    72.  
    73. [MenuItem("Window/Mirror Animation Curves")]
    74. public static void ShowWindow() {
    75.     EditorWindow.GetWindow(typeof(mirror_animation_curves));
    76. }
    77.  
    78. const string prefs_prefix="Ilya_MirrorAnimationCurves_";
    79.  
    80. string from_root="";
    81. string from_prefix="";
    82. string from_appendage="";
    83. string to_root="";
    84. string to_prefix="";
    85. string to_appendage="";
    86.  
    87. void OnEnable() {
    88.     from_root=EditorPrefs.GetString(prefs_prefix +"FromRoot");
    89.     from_prefix=EditorPrefs.GetString(prefs_prefix +"FromPrefix");
    90.     from_appendage=EditorPrefs.GetString(prefs_prefix +"FromAppendage");
    91.     to_root=EditorPrefs.GetString(prefs_prefix +"ToRoot");
    92.     to_prefix=EditorPrefs.GetString(prefs_prefix +"ToPrefix");
    93.     to_appendage=EditorPrefs.GetString(prefs_prefix +"ToAppendage");
    94. }
    95.  
    96. void OnDisable() {
    97.     EditorPrefs.SetString(prefs_prefix +"FromRoot", from_root);
    98.     EditorPrefs.SetString(prefs_prefix +"FromPrefix", from_prefix);
    99.     EditorPrefs.SetString(prefs_prefix +"FromAppendage", from_appendage);
    100.     EditorPrefs.SetString(prefs_prefix +"ToRoot", to_root);
    101.     EditorPrefs.SetString(prefs_prefix +"ToPrefix", to_prefix);
    102.     EditorPrefs.SetString(prefs_prefix +"ToAppendage", to_appendage);
    103. }
    104.  
    105. void mirror_curves(AnimationClip targ, int axis) {
    106.     string position_name;
    107.     string rotation_a_name;
    108.     string rotation_b_name;
    109.     if (axis==0) {
    110.         position_name="m_LocalPosition.x";
    111.         rotation_a_name="m_LocalRotation.y";
    112.         rotation_b_name="m_LocalRotation.z";
    113.     } else
    114.     if (axis==1) {
    115.         position_name="m_LocalPosition.y";
    116.         rotation_a_name="m_LocalRotation.x";
    117.         rotation_b_name="m_LocalRotation.z";
    118.     } else {
    119.         position_name="m_LocalPosition.z";
    120.         rotation_a_name="m_LocalRotation.x";
    121.         rotation_b_name="m_LocalRotation.y";
    122.     }
    123.     var from_root_split=from_root.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
    124.     var curves=AnimationUtility.GetAllCurves(targ);
    125.     var new_curves=new List<AnimationCurve>();
    126.     var new_paths=new List<string>();
    127.     var new_property_names=new List<string>();
    128.     foreach (var c in curves) {
    129.         var c_split=c.path.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
    130.         {
    131.             var good=true;
    132.             for (int x=0;x<from_root_split.Length;++x) {
    133.                 if (
    134.                     x>=c_split.Length ||
    135.                     c_split[x]!=from_root_split[x]
    136.                 ) {
    137.                     good=false;
    138.                     break;
    139.                 }
    140.             }
    141.             if (!good) continue;
    142.         }
    143.         {
    144.             var t_keys=c.curve.keys;
    145.             if (c.type==typeof(Transform)  axis!=-1  (
    146.                 c.propertyName==position_name ||
    147.                 c.propertyName==rotation_a_name ||
    148.                 c.propertyName==rotation_b_name
    149.             )) {
    150.                 for (int x=0;x<t_keys.Length;++x) {
    151.                     t_keys[x].value=-t_keys[x].value;
    152.                     t_keys[x].inTangent=-t_keys[x].inTangent;
    153.                     t_keys[x].outTangent=-t_keys[x].outTangent;
    154.                 }
    155.             }
    156.             var new_curve=new AnimationCurve(t_keys);
    157.             var new_c_split=new string[c_split.Length-from_root_split.Length];
    158.             for (var x=from_root_split.Length;x<c_split.Length;++x) {
    159.                 var d=c_split[x];
    160.                 if (d.StartsWith(from_prefix)) {
    161.                     d=d.Remove(0, from_prefix.Length);
    162.                 }
    163.                 if (d.EndsWith(from_appendage)) {
    164.                     d=d.Remove(d.Length-from_appendage.Length);
    165.                 }
    166.                 d=to_prefix + d + to_appendage;
    167.                 new_c_split[x-from_root_split.Length]=d;
    168.             }
    169.             var new_path=to_root +"/"+ string.Join("/", new_c_split);
    170.             new_paths.Add(new_path);
    171.             new_curves.Add(new_curve);
    172.             new_property_names.Add(c.propertyName);
    173.         }
    174.     }
    175.     for (int x=0;x<new_curves.Count;++x) {
    176.         targ.SetCurve(new_paths[x], typeof(Transform), new_property_names[x], new_curves[x]);
    177.     }
    178. }
    179.  
    180. void OnGUI() {
    181.     GUILayout.Label("Make sure the currently selected gameobject has the");
    182.     GUILayout.Label("clip you're going to operate on as its default clip.");
    183.     GUILayout.Label("View the script source code for more documentation.");
    184.     GUILayout.Label("From root: (e.g. \"right_arm\", \"body/right_arm\" or \"Arm.R\")");
    185.     from_root=GUILayout.TextField(from_root);
    186.     //
    187.     GUILayout.Label("From prefix: (e.g. \"right_\", or \"\")");
    188.     from_prefix=GUILayout.TextField(from_prefix);
    189.     //
    190.     GUILayout.Label("From appendage: (e.g. \"_0\", \".R\", or \"\")");
    191.     from_appendage=GUILayout.TextField(from_appendage);
    192.     //
    193.     GUILayout.Label("To root: (e.g. \"left_arm\", \"body/left_arm\", or \"Arm.L\")");
    194.     to_root=GUILayout.TextField(to_root);
    195.     //
    196.     GUILayout.Label("To prefix: (e.g. \"left_\", or \"\")");
    197.     to_prefix=GUILayout.TextField(to_prefix);
    198.     //
    199.     GUILayout.Label("To appendage: (e.g. \"_0\", \".L\", or \"\")");
    200.     to_appendage=GUILayout.TextField(to_appendage);
    201.     //
    202.     var copy=GUILayout.Button("Copy (no mirroring)");
    203.     var mirror_x=GUILayout.Button("Mirror X");
    204.     var mirror_y=GUILayout.Button("Mirror Y");
    205.     var mirror_z=GUILayout.Button("Mirror Z");
    206.     var mirror_index=-2;
    207.     if (copy) mirror_index=-1;
    208.     else if (mirror_x) mirror_index=0;
    209.     else if (mirror_y) mirror_index=1;
    210.     else if (mirror_z) mirror_index=2;
    211.     if (mirror_index!=-2) {
    212.         var s=Selection.activeGameObject;
    213.         if (s==null) {
    214.             Debug.LogError("No object was selected.");
    215.             return;
    216.         }
    217.         var a=s.GetComponent<Animation>();
    218.         if (a==null) {
    219.             Debug.LogError("That object does not have an animation.");
    220.             return;
    221.         }
    222.         var c=a.clip;
    223.         if (c==null) {
    224.             Debug.LogError("The default animation clip is null.");
    225.             return;
    226.         }
    227.         Debug.Log("Mirroring/copying animation curves on animation clip \""+ c.name +"\".");
    228.         Undo.RegisterUndo(c, "Mirror Animation Curves");
    229.         mirror_curves(c, mirror_index);
    230.     }
    231. }
    232.  
    233.  
    234. };
    235.  
     
    Last edited: Sep 17, 2012
  2. Akshay_ROG

    Akshay_ROG

    Joined:
    Sep 13, 2013
    Posts:
    48
    This is good brother, I found it when I was googling how to mirror generic animations in unity, when I set my model's rig as humanoid something gets wrong with his legs and when I set it to generic, animations and legs are fine. So my problem is (would u help me on this) how to mirror generic animations, I got like 13 super-complex animations to mirror, these animations let me give randomness to the gameplay and how the player controlled ped behaves. I don't wanna ditch this notion of randomness b'coz it is one of my core gameplay feature.

    Mirroring animations in Blender too phucks up them because I forgot to reset rest pose in past and unfortunately saved it that way, later when I discovered this mistake it just sucked you know and now I can not do mirror in any app (making new animations for other side is spine breaking job which I suspect is inevitable):eek::-x:neutral::confused::(:mad: And yeah thankyou.


    OH S*** A YEAR OLD POST!!!
     
    Last edited: Nov 20, 2013
  3. DugganSC

    DugganSC

    Joined:
    Feb 6, 2014
    Posts:
    7
    :-D Much later, I ran into this post. For those trying to adapt the script, the condition at line 145 seems to be missing the && indicators.
     
    swingingtom likes this.