Search Unity

Animation Event Copier

Discussion in 'Works In Progress' started by WellFiredDevelopment, Jun 17, 2012.

  1. WellFiredDevelopment

    WellFiredDevelopment

    Joined:
    Jun 16, 2012
    Posts:
    659
    Hey guys,

    I don't know if this is the correct place, however here's a small chunk of code I found incredibly useful! It moves all animation events from one animationClip to another.

    Very simple, but very useful.

    Code (csharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using System.Reflection;
    5.  
    6. public class AnimationEventCopier : EditorWindow
    7. {
    8.     private AnimationClip sourceObject;
    9.     private AnimationClip targetObject;
    10. [MenuItem("Window/Animation Event Copier")]
    11. static void Init()
    12. {
    13.         GetWindow(typeof(AnimationEventCopier));
    14. }
    15. void OnGUI()
    16. {
    17.         EditorGUILayout.BeginHorizontal();
    18.             sourceObject = EditorGUILayout.ObjectField("Source", sourceObject, typeof(AnimationClip), true) as AnimationClip;
    19.         EditorGUILayout.EndHorizontal();
    20.         EditorGUILayout.BeginHorizontal();
    21.             targetObject = EditorGUILayout.ObjectField("Target", targetObject, typeof(AnimationClip), true) as AnimationClip;
    22.         EditorGUILayout.EndHorizontal();
    23.  
    24.         if (sourceObject != null  targetObject != null)
    25.         {
    26.             EditorGUILayout.BeginHorizontal();
    27.                 if (GUILayout.Button("Copy"))
    28.                     CopyData();
    29.             EditorGUILayout.EndHorizontal();
    30.         }
    31. }
    32.  
    33.     void CopyData()
    34.     {
    35.         Undo.RegisterUndo(targetObject, "Undo Generic Copy");
    36.  
    37.         AnimationClip sourceAnimClip = sourceObject as AnimationClip;
    38.         AnimationClip targetAnimClip = targetObject as AnimationClip;
    39.  
    40.         if(sourceAnimClip  targetAnimClip)
    41.         {
    42.             AnimationUtility.SetAnimationEvents(targetAnimClip, null);
    43.             AnimationUtility.SetAnimationEvents(targetAnimClip, AnimationUtility.GetAnimationEvents(sourceAnimClip));
    44.         }
    45.     }
    46. }
     
    Rodolinc, damikuru and infinitypbr like this.
  2. 3d_Artist1987

    3d_Artist1987

    Joined:
    Jul 9, 2012
    Posts:
    710
    hi,

    Very useful script.

    Dev
     
  3. jiovanie

    jiovanie

    Joined:
    Feb 15, 2013
    Posts:
    3
    Two years later, I find this and I thank you :D
     
  4. roberto-ardila

    roberto-ardila

    Unity Technologies

    Joined:
    Apr 8, 2013
    Posts:
    5
    Thanks a lot this was a life saver!
     
  5. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    2,839
    This is great! However, I'm attempting to expand the script and not sure why it isn't working. I've added an option for copying all clips from one game object to another. I see that only animations from an animator are showing, so I'm not sure why that's the case.

    However, even when I do use two in-scene objects with animators (the old and the new version), The clips aren't copied.

    Anyone know what I'm doing wrong?

    EDIT: I figured it out. See next post.
     
    Last edited: Apr 2, 2017
  6. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    2,839
    Alright here's the code! It now has an option for copying all the clips from one GameObject to another. However, it uses the Animator Controller for this. The names of the clips in the Animator are used and need to match. In my case, and I'm thinking perhaps in other peoples cases, this script is useful when a models animations have been updated.

    In that case, make a copy of the original Animator, and replace the animation clips assigned to each "Animation" in the graph with the new clips. If the names of the clips do not match, there are two Utility buttons that will make them match. (This is dangerous only in that it doesn't care what the current names are, so if you have two sets of animations that aren't supposed to be 1-to-1 matches, you could end up with weird names)

    Then the bulk copy will work.

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using System.Reflection;
    5.  
    6. /// <summary>
    7. /// Originally Posted by ArtOfSettling on the Unity Forums:  https://forum.unity3d.com/threads/animation-event-copier.140158/
    8. ///
    9. /// Modified by S.F. Bay Studios (http://www.InfinityPBR.com)
    10. /// </summary>
    11.  
    12. public class AnimationEventCopier : EditorWindow
    13. {
    14.     private AnimationClip sourceObject;                                    // Source Clip
    15.     private AnimationClip targetObject;                                    // Target Clip
    16.     private GameObject sourceGameObject;                                // Source Game Object
    17.     private GameObject targetGameObject;                                // Target Game Object
    18.  
    19.     private Vector2 scrollPos;
    20.  
    21.     [MenuItem("Window/Animation Event Copier")]                            // Can load this via the window menu
    22.     static void Init()
    23.     {
    24.         GetWindow(typeof(AnimationEventCopier));                        // Brings this window to the front
    25.     }
    26.     void OnGUI()
    27.     {
    28.         scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
    29.         // Show a title box and object fields
    30.         EditorGUILayout.HelpBox ("SINGLE CLIP COPY\n\nChoose a source clip and a target clip.  Names do not need to match.  The " +
    31.             "Animation Events and Clip Settings will be copied from the source to the target.", MessageType.Info);
    32.         EditorGUILayout.BeginHorizontal();
    33.         sourceObject = EditorGUILayout.ObjectField("Source", sourceObject, typeof(AnimationClip), true) as AnimationClip;
    34.         targetObject = EditorGUILayout.ObjectField("Target", targetObject, typeof(AnimationClip), true) as AnimationClip;
    35.         EditorGUILayout.EndHorizontal();
    36.  
    37.         // If both objects are present, show the copy button
    38.         if (sourceObject != null && targetObject != null)
    39.         {
    40.             if (GUILayout.Button ("Copy Single Clip")) {
    41.                 Undo.RecordObject(targetObject, "Undo Clip Event Copy");
    42.                 CopyData (sourceObject, targetObject);
    43.             }
    44.             EditorGUILayout.HelpBox ("Source Event Count: " + AnimationUtility.GetAnimationEvents (sourceObject).Length + "\n\n" +
    45.                 "Target Event Count: " + AnimationUtility.GetAnimationEvents (targetObject).Length, MessageType.Info);
    46.         }
    47.  
    48.         EditorGUILayout.Space ();
    49.         EditorGUILayout.Space ();
    50.         EditorGUILayout.Space ();
    51.  
    52.         // Show a title box and object fields
    53.         EditorGUILayout.HelpBox ("ALL CLIP COPY\n\nChoose a source GameObject and a target GameObject.  For all clips where the names" +
    54.             " match, the Events & Clip Settings will be copied.\n\nNOTE:  \"Name\" of the clip is taken from the Animator Controller component, " +
    55.             "as is the Animation Clip.  Because of this, each GameObject should have a *different* Animator Component attached.", MessageType.Info);
    56.         EditorGUILayout.BeginHorizontal();
    57.         sourceGameObject = EditorGUILayout.ObjectField("Source", sourceGameObject, typeof(GameObject), true) as GameObject;
    58.         targetGameObject = EditorGUILayout.ObjectField("Target", targetGameObject, typeof(GameObject), true) as GameObject;
    59.         EditorGUILayout.EndHorizontal();
    60.  
    61.         // If both objects are present, show the copy button
    62.         if (sourceGameObject != null && sourceGameObject != null)
    63.         {
    64.             if (GUILayout.Button ("Copy All Clips")) {
    65.                 Undo.RecordObject(targetGameObject, "Undo Clip Event Copy");
    66.                 CopyAllData (sourceGameObject, targetGameObject);
    67.             }
    68.             EditorGUILayout.HelpBox ("Source Clip Count: " + AnimationUtility.GetAnimationClips (sourceGameObject).Length + "\n\n" +
    69.                 "Target Clip Count: " + AnimationUtility.GetAnimationClips (targetGameObject).Length, MessageType.Info);
    70.  
    71.             /* Uncoment this section if you'd like to see a list of each Clip with the number of events on it.  Good for debugging
    72.             for (int i = 0; i < AnimationUtility.GetAnimationClips (sourceGameObject).Length; i++) {
    73.                 EditorGUILayout.LabelField ("Source Clip " + AnimationUtility.GetAnimationClips (sourceGameObject) [i].name, AnimationUtility.GetAnimationEvents (AnimationUtility.GetAnimationClips (sourceGameObject) [i]).Length + " Events");
    74.             }
    75.             for (int i = 0; i < AnimationUtility.GetAnimationClips (targetGameObject).Length; i++) {
    76.                 EditorGUILayout.LabelField ("Target Clip " + AnimationUtility.GetAnimationClips (targetGameObject) [i].name, AnimationUtility.GetAnimationEvents (AnimationUtility.GetAnimationClips (targetGameObject) [i]).Length + " Events");
    77.             }  
    78.             */
    79.         }
    80.  
    81.         EditorGUILayout.Space ();
    82.         EditorGUILayout.Space ();
    83.         EditorGUILayout.Space ();
    84.  
    85.         // Show a title box and object fields
    86.         EditorGUILayout.HelpBox ("UTILITIES\n\nThese buttons may be of use.  Backup your project first, if you're not sure!", MessageType.Info);
    87.         if (GUILayout.Button ("Copy All SOURCE Names to TARGET")) {
    88.             if (EditorUtility.DisplayDialog ("Copy Source Names", "Are you sure you want to copy all SOURCE names to TARGET?", "Confirm", "Cancel")) {
    89.                 CopyClipNames (sourceGameObject, targetGameObject);
    90.             }
    91.         }
    92.         if (GUILayout.Button ("Copy All TARGET Names to SOURCE")) {
    93.             if (EditorUtility.DisplayDialog ("Copy Target Names", "Are you sure you want to copy all TARGET names to SOURCE?", "Confirm", "Cancel")) {
    94.                 CopyClipNames (targetGameObject, sourceGameObject);
    95.             }
    96.         }
    97.         EditorGUILayout.EndScrollView ();
    98.     }
    99.  
    100.     /// <summary>
    101.     /// This will copy the names from one to the other.  It's somewhat dangerous, as it just goes by the number of clips.  There's no way to
    102.     /// confirm that the names are SUPPOSED to be overwritten.  The intent is that one Animator Component is a copy of the other, each with
    103.     /// one set of clips (the old and the new).  However, the clip names may have changed in the process, and this is faster than doing it
    104.     /// by hand.
    105.     /// </summary>
    106.     /// <param name="copyFrom">Copy from.</param>
    107.     /// <param name="copyTo">Copy to.</param>
    108.     void CopyClipNames(GameObject copyFrom, GameObject copyTo){
    109.         AnimationClip[] fromClips = AnimationUtility.GetAnimationClips (copyFrom);                                // Get all source clips
    110.         AnimationClip[] toClips = AnimationUtility.GetAnimationClips (copyTo);                                    // Get all target Clips
    111.         for (int i = 0; i < fromClips.Length; i++) {
    112.             if (toClips.Length >= i - 1) {
    113.                 string oldName = toClips [i].name;
    114.                 toClips [i].name = fromClips[i].name;
    115.                 Debug.Log(oldName + " is now " + toClips [i].name);
    116.             }
    117.         }
    118.     }
    119.  
    120.     /// <summary>
    121.     /// Copies the events from sourceAnimClip to targetAnimClip
    122.     /// </summary>
    123.     /// <param name="sourceAnimClip">Source animation clip.</param>
    124.     /// <param name="targetAnimClip">Target animation clip.</param>
    125.     void CopyData(AnimationClip sourceClip, AnimationClip targetClip)
    126.     {
    127.         AnimationUtility.SetAnimationEvents(targetClip, null);                                                                // Remove all events
    128.         AnimationUtility.SetAnimationEvents(targetClip, AnimationUtility.GetAnimationEvents (sourceClip));                    // Copy Events
    129.         AnimationUtility.SetAnimationClipSettings(targetClip, AnimationUtility.GetAnimationClipSettings(sourceClip));        // Copy Clip Settings
    130.         Debug.Log ("Copying " + AnimationUtility.GetAnimationEvents (sourceClip).Length + " event from " + sourceClip.name + " to " + targetClip.name + " (" + AnimationUtility.GetAnimationEvents (targetClip).Length + " events)");
    131.     }
    132.  
    133.     /// <summary>
    134.     /// Searches the source matched clip names in target, and will copy the data over.
    135.     /// </summary>
    136.     /// <param name="source">Source.</param>
    137.     /// <param name="target">Target.</param>
    138.     void CopyAllData(GameObject source, GameObject target){
    139.         AnimationClip[] animationClipsSource = AnimationUtility.GetAnimationClips (source);                                    // Get all source clips
    140.         AnimationClip[] animationClipsTarget = AnimationUtility.GetAnimationClips (target);                                    // Get all target Clips
    141.         Debug.Log ("Copying data from " + animationClipsSource.Length + " clips in " + source.name + " to " + animationClipsTarget.Length + " clips in " + target.name);
    142.         for (int i = 0; i < animationClipsSource.Length; i++) {                                                                // For each source Clip...
    143.             bool foundMatch = false;                                                                                        // Check to make sure we found a match
    144.             for (int n = 0; n < animationClipsTarget.Length; n++) {                                                            // ... & For each target clip...
    145.                 if (animationClipsSource [i].name == animationClipsTarget [n].name) {                                        // ...If the names match
    146.                     CopyData (animationClipsSource [i], animationClipsTarget [n]);                                            // Copy data
    147.                     foundMatch = true;                                                                                        // Toggle true
    148.                 }
    149.             }
    150.             if (!foundMatch) {
    151.                 // Give a warning to the user, in case they have a spelling error or something.
    152.                 Debug.LogWarning ("Warning: There was no matching clip called " + animationClipsSource [i].name + " found.  " +
    153.                 "(Target name for this ID was " + animationClipsTarget [i].name + ")");
    154.             }
    155.         }
    156.     }
    157. }
     
    damikuru likes this.
  7. itchyOwl

    itchyOwl

    Joined:
    Feb 28, 2013
    Posts:
    25
    Hey, I made a simple tool that allows you to define all animation settings in presets (implemented as ScriptableObjects). These presets are then used to apply the settings to clips in one or many animation assets. It's a bit different approach to the methods above, but allows you to do much more.

    So, in practice, you don't have to define the settings for each clip seprately. Instead, you can use your own naming conventions to group the clips into types and apply different settings to these groups procedurally.

    The definitions has to be done only once. When the animation asset is changed and needs to be updated, simply hook the asset and the preset(s) in the tool and let it run. It will then setup the animation events and the other settings according to the preset definitions.

    Check it out at here.
     
    infinitypbr likes this.
  8. realblues

    realblues

    Joined:
    Dec 10, 2010
    Posts:
    10
    Wow!!

    infinitypbr and itchyOwl you guys saves my life.

    both are what I needed!! I can even buy this.

    thanks so much
     
    infinitypbr likes this.
  9. Rodolinc

    Rodolinc

    Joined:
    Sep 23, 2013
    Posts:
    62
    Why this didnt work until I removed line 42? "AnimationUtility.SetAnimationEvents(targetAnimClip, null);"
    (Unity2019.2.0f1)