using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.Linq; public enum ParameterType { NONE = 0, INT = 1, FLOAT = 2, STRING = 3, OBJECT = 4, ALL = 5 } public class AnimationEventEditor : EditorWindow { static private AnimationClip sourceObject; static readonly List events = new List(); static readonly List parameterTypes = new List(); [MenuItem("Window/Animation Event Editor")] static void Init() { GetWindow(typeof(AnimationEventEditor)); } private void Reload() { if(sourceObject) { events.Clear(); parameterTypes.Clear(); for (int i = 0; i < sourceObject.events.Length; i++) { events.Add(sourceObject.events[i]); if(string.IsNullOrEmpty(sourceObject.events[i].stringParameter) == false) parameterTypes.Add(ParameterType.STRING); else if (sourceObject.events[i].intParameter != 0) parameterTypes.Add(ParameterType.INT); else if (sourceObject.events[i].floatParameter != 0) parameterTypes.Add(ParameterType.FLOAT); else if (sourceObject.events[i].objectReferenceParameter != null) parameterTypes.Add(ParameterType.OBJECT); else parameterTypes.Add(ParameterType.NONE); } } } void OnGUI() { EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); sourceObject = EditorGUILayout.ObjectField("Source", sourceObject, typeof(AnimationClip), true) as AnimationClip; Selection.activeObject = AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(sourceObject)); if (EditorGUI.EndChangeCheck()) Reload(); EditorGUILayout.EndHorizontal(); if (events.Count > 0) { GUILayout.Space(10); GUILayout.Label($"{sourceObject.name} Events", EditorStyles.boldLabel); GUILayout.Space(10); GUIStyle rightAligned = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.UpperRight }; EditorGUI.indentLevel++; int markedForDeletion = -1; for(int i = 0; i < events.Count; i++) { AnimationEvent animationEvent = events[i]; EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("X", GUILayout.Width(20))) markedForDeletion = i; EditorGUILayout.BeginVertical(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(5); events[i].functionName = EditorGUILayout.TextField(events[i].functionName, EditorStyles.textField); GUILayout.Label($"@{animationEvent.time} s", rightAligned); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); parameterTypes[i] = (ParameterType)EditorGUILayout.EnumPopup("Parameter : ", parameterTypes[i], GUILayout.Width(300)); switch(parameterTypes[i]) { case ParameterType.INT: events[i].intParameter = EditorGUILayout.IntField(events[i].intParameter, EditorStyles.textField); break; case ParameterType.FLOAT: events[i].floatParameter = EditorGUILayout.FloatField(events[i].floatParameter, EditorStyles.textField); break; case ParameterType.STRING: events[i].stringParameter = EditorGUILayout.TextField(events[i].stringParameter, EditorStyles.textField); break; case ParameterType.OBJECT: events[i].objectReferenceParameter = EditorGUILayout.ObjectField(events[i].objectReferenceParameter, typeof(Object), true); break; case ParameterType.ALL: EditorGUILayout.BeginVertical(); events[i].floatParameter = EditorGUILayout.FloatField("float", events[i].floatParameter, EditorStyles.textField); events[i].intParameter = EditorGUILayout.IntField("int", events[i].intParameter, EditorStyles.textField); events[i].stringParameter = EditorGUILayout.TextField("string", events[i].stringParameter, EditorStyles.textField); events[i].objectReferenceParameter = EditorGUILayout.ObjectField("object", events[i].objectReferenceParameter, typeof(Object), true); EditorGUILayout.EndVertical(); break; } EditorGUILayout.EndHorizontal(); EditorGUI.BeginChangeCheck(); events[i].time = EditorGUILayout.Slider("Time", events[i].time, 0, sourceObject.length); if (EditorGUI.EndChangeCheck()) { Debug.Log(events[i].functionName + " : " + events[i].time.ToString()); } animationEvent.time = events[i].time; EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); GUILayout.Label(""); } if(markedForDeletion >= 0) { events.RemoveAt(markedForDeletion); parameterTypes.RemoveAt(markedForDeletion); } EditorGUI.indentLevel--; } if (sourceObject != null) { GUILayout.Space(10); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Add Event", GUILayout.Height(32))) DoAddEvent(); GUILayout.Space(10); if (GUILayout.Button("Save", GUILayout.Height(32))) SaveClip(); GUILayout.Space(10); if (GUILayout.Button("Reload", GUILayout.Height(32))) Reload(); EditorGUILayout.EndHorizontal(); GUILayout.Space(10); } } static void DoAddEvent() { events.Add(new AnimationEvent() { functionName = "New Event", time = 0 }); parameterTypes.Add(ParameterType.NONE); } static void SaveClip() { for(int i = 0; i < events.Count; i++) { switch (parameterTypes[i]) { case ParameterType.INT: events[i].stringParameter = string.Empty; events[i].floatParameter = 0; events[i].objectReferenceParameter = null; break; case ParameterType.FLOAT: events[i].intParameter = 0; events[i].stringParameter = string.Empty; events[i].objectReferenceParameter = null; break; case ParameterType.STRING: events[i].intParameter = 0; events[i].floatParameter = 0; events[i].objectReferenceParameter = null; break; case ParameterType.OBJECT: events[i].intParameter = 0; events[i].stringParameter = string.Empty; events[i].floatParameter = 0; break; case ParameterType.NONE: events[i].intParameter = 0; events[i].stringParameter = string.Empty; events[i].floatParameter = 0; events[i].objectReferenceParameter = null; break; } } if ((sourceObject.hideFlags & HideFlags.NotEditable) != 0) DoAddEventImportedClip(sourceObject); else DoAddEventClip(sourceObject); } static void DoAddEventClip(AnimationClip sourceClip) { AnimationEvent[] sourceAnimEvents = AnimationUtility.GetAnimationEvents(sourceClip); AnimationUtility.SetAnimationEvents(sourceClip, sourceAnimEvents); } static void DoAddEventImportedClip(AnimationClip targetAnimClip) { ModelImporter modelImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(targetAnimClip)) as ModelImporter; if (modelImporter == null) return; SerializedObject serializedObject = new SerializedObject(modelImporter); SerializedProperty clipAnimations = serializedObject.FindProperty("m_ClipAnimations"); if (!clipAnimations.isArray) return; for (int i = 0; i < clipAnimations.arraySize; i++) { AnimationClipInfoProperties clipInfoProperties = new AnimationClipInfoProperties(clipAnimations.GetArrayElementAtIndex(i)); if (clipInfoProperties.name == targetAnimClip.name) { AnimationEvent[] sourceAnimEvents = events.ToArray(); foreach (AnimationEvent animationEvent in sourceAnimEvents) { float lastTime = animationEvent.time; animationEvent.time *= 1f / sourceObject.length; Debug.Log($"{animationEvent.functionName} changed to {animationEvent.time} from {lastTime}"); } clipInfoProperties.SetEvents(sourceAnimEvents); serializedObject.ApplyModifiedProperties(); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(targetAnimClip)); break; } } } }