using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.Reflection; /// /// Originally Posted by ArtOfSettling on the Unity Forums: https://forum.unity3d.com/threads/animation-event-copier.140158/ /// /// Modified by S.F. Bay Studios (http://www.InfinityPBR.com) /// public class AnimationEventCopier : EditorWindow { private AnimationClip sourceObject; // Source Clip private AnimationClip targetObject; // Target Clip private GameObject sourceGameObject; // Source Game Object private GameObject targetGameObject; // Target Game Object private GameObject eraseObject; // Target to erase Events private Vector2 scrollPos; [MenuItem("Window/Infinity PBR/Animation Event Manager")] // Can load this via the window menu static void Init() { GetWindow(typeof(AnimationEventCopier)); // Brings this window to the front } void OnGUI() { scrollPos = EditorGUILayout.BeginScrollView(scrollPos); // Show a title box and object fields EditorGUILayout.HelpBox ("SINGLE CLIP COPY\n\nChoose a source clip and a target clip. Names do not need to match. The " + "Animation Events and Clip Settings will be copied from the source to the target.", MessageType.Info); EditorGUILayout.BeginHorizontal(); sourceObject = EditorGUILayout.ObjectField("Source", sourceObject, typeof(AnimationClip), true) as AnimationClip; targetObject = EditorGUILayout.ObjectField("Target", targetObject, typeof(AnimationClip), true) as AnimationClip; EditorGUILayout.EndHorizontal(); // If both objects are present, show the copy button if (sourceObject != null && targetObject != null) { if (GUILayout.Button ("Copy Single Clip")) { Undo.RecordObject(targetObject, "Undo Clip Event Copy"); CopyData (sourceObject, targetObject); } EditorGUILayout.HelpBox ("Source Event Count: " + AnimationUtility.GetAnimationEvents (sourceObject).Length + "\n\n" + "Target Event Count: " + AnimationUtility.GetAnimationEvents (targetObject).Length, MessageType.Info); } EditorGUILayout.Space (); EditorGUILayout.Space (); EditorGUILayout.Space (); // Show a title box and object fields EditorGUILayout.HelpBox ("ALL CLIP COPY\n\nChoose a source GameObject and a target GameObject. For all clips where the names" + " match, the Events & Clip Settings will be copied.\n\nNOTE: \"Name\" of the clip is taken from the Animator Controller component, " + "as is the Animation Clip. Because of this, each GameObject should have a *different* Animator Component attached.", MessageType.Info); EditorGUILayout.BeginHorizontal(); sourceGameObject = EditorGUILayout.ObjectField("Source", sourceGameObject, typeof(GameObject), true) as GameObject; targetGameObject = EditorGUILayout.ObjectField("Target", targetGameObject, typeof(GameObject), true) as GameObject; EditorGUILayout.EndHorizontal(); // If both objects are present, show the copy button if (sourceGameObject != null && sourceGameObject != null) { if (GUILayout.Button ("Copy All Clips")) { Undo.RecordObject(targetGameObject, "Undo Clip Event Copy"); CopyAllData (sourceGameObject, targetGameObject); } EditorGUILayout.HelpBox ("Source Clip Count: " + AnimationUtility.GetAnimationClips (sourceGameObject).Length + "\n\n" + "Target Clip Count: " + AnimationUtility.GetAnimationClips (targetGameObject).Length, MessageType.Info); /* Uncoment this section if you'd like to see a list of each Clip with the number of events on it. Good for debugging for (int i = 0; i < AnimationUtility.GetAnimationClips (sourceGameObject).Length; i++) { EditorGUILayout.LabelField ("Source Clip " + AnimationUtility.GetAnimationClips (sourceGameObject) [i].name, AnimationUtility.GetAnimationEvents (AnimationUtility.GetAnimationClips (sourceGameObject) [i]).Length + " Events"); } for (int i = 0; i < AnimationUtility.GetAnimationClips (targetGameObject).Length; i++) { EditorGUILayout.LabelField ("Target Clip " + AnimationUtility.GetAnimationClips (targetGameObject) [i].name, AnimationUtility.GetAnimationEvents (AnimationUtility.GetAnimationClips (targetGameObject) [i]).Length + " Events"); } */ } EditorGUILayout.Space (); EditorGUILayout.Space (); EditorGUILayout.Space (); // Show a title box and object fields EditorGUILayout.HelpBox ("UTILITIES\n\nThese buttons may be of use. Backup your project first, if you're not sure!", MessageType.Info); if (GUILayout.Button ("Copy All SOURCE Names to TARGET")) { if (EditorUtility.DisplayDialog ("Copy Source Names", "Are you sure you want to copy all SOURCE names to TARGET?", "Confirm", "Cancel")) { CopyClipNames (sourceGameObject, targetGameObject); } } EditorGUILayout.Space (); if (GUILayout.Button ("Copy All TARGET Names to SOURCE")) { if (EditorUtility.DisplayDialog ("Copy Target Names", "Are you sure you want to copy all TARGET names to SOURCE?", "Confirm", "Cancel")) { CopyClipNames (targetGameObject, sourceGameObject); } } EditorGUILayout.Space (); EditorGUILayout.BeginHorizontal(); eraseObject = EditorGUILayout.ObjectField("Object to Erase", eraseObject, typeof(GameObject), true) as GameObject; if (eraseObject) { if (GUILayout.Button ("Erase All Events")) { if (EditorUtility.DisplayDialog ("Erase Events", "Are you sure you want to Erase All Events?", "Confirm", "Cancel")) { EraseEvents (eraseObject); } } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndScrollView (); } void EraseEvents(GameObject targetObject){ AnimationClip[] targetClips = AnimationUtility.GetAnimationClips (targetObject); for (int i = 0; i < targetClips.Length; i++) { EraseEvent (targetClips [i]); } } void EraseEvent(AnimationClip targetClip){ AnimationUtility.SetAnimationEvents(targetClip, null); } /// /// 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 /// confirm that the names are SUPPOSED to be overwritten. The intent is that one Animator Component is a copy of the other, each with /// 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 /// by hand. /// /// Copy from. /// Copy to. void CopyClipNames(GameObject copyFrom, GameObject copyTo){ AnimationClip[] fromClips = AnimationUtility.GetAnimationClips (copyFrom); // Get all source clips AnimationClip[] toClips = AnimationUtility.GetAnimationClips (copyTo); // Get all target Clips for (int i = 0; i < fromClips.Length; i++) { if (toClips.Length >= i - 1) { string oldName = toClips [i].name; toClips [i].name = fromClips[i].name; Debug.Log(oldName + " is now " + toClips [i].name); } } } /// /// Copies the events from sourceAnimClip to targetAnimClip /// /// Source animation clip. /// Target animation clip. void CopyData(AnimationClip sourceClip, AnimationClip targetClip) { EraseEvent (targetClip); // Remove all events AnimationUtility.SetAnimationEvents(targetClip, AnimationUtility.GetAnimationEvents (sourceClip)); // Copy Events AnimationUtility.SetAnimationClipSettings(targetClip, AnimationUtility.GetAnimationClipSettings(sourceClip)); // Copy Clip Settings Debug.Log ("Copying " + AnimationUtility.GetAnimationEvents (sourceClip).Length + " event from " + sourceClip.name + " to " + targetClip.name + " (" + AnimationUtility.GetAnimationEvents (targetClip).Length + " events)"); } /// /// Searches the source matched clip names in target, and will copy the data over. /// /// Source. /// Target. void CopyAllData(GameObject source, GameObject target){ AnimationClip[] animationClipsSource = AnimationUtility.GetAnimationClips (source); // Get all source clips AnimationClip[] animationClipsTarget = AnimationUtility.GetAnimationClips (target); // Get all target Clips Debug.Log ("Copying data from " + animationClipsSource.Length + " clips in " + source.name + " to " + animationClipsTarget.Length + " clips in " + target.name); for (int i = 0; i < animationClipsSource.Length; i++) { // For each source Clip... bool foundMatch = false; // Check to make sure we found a match for (int n = 0; n < animationClipsTarget.Length; n++) { // ... & For each target clip... if (animationClipsSource [i].name == animationClipsTarget [n].name) { // ...If the names match CopyData (animationClipsSource [i], animationClipsTarget [n]); // Copy data foundMatch = true; // Toggle true } } if (!foundMatch) { // Give a warning to the user, in case they have a spelling error or something. Debug.LogWarning ("Warning: There was no matching clip called " + animationClipsSource [i].name + " found. " + "(Target name for this ID was " + animationClipsTarget [i].name + ")"); } } } }