public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) { AnimationWindowEventData data = GetData(awEvents); if (data.events == null || data.selectedEvents == null || data.selectedEvents.Length == 0) { return; } AnimationEvent firstEvent = data.selectedEvents[0]; bool singleFunctionName = Array.TrueForAll(data.selectedEvents, evt => evt.functionName == firstEvent.functionName); GUI.changed = false; if (data.root != null) { List <AnimationWindowEventMethod> methods = new List <AnimationWindowEventMethod>(); HashSet <string> overloads = new HashSet <string>(); CollectSupportedMethods(data.root, methods, overloads); var methodsFormatted = new List <string>(methods.Count); for (int i = 0; i < methods.Count; ++i) { AnimationWindowEventMethod method = methods[i]; string postFix = " ( )"; if (method.parameterType != null) { if (method.parameterType == typeof(float)) { postFix = " ( float )"; } else if (method.parameterType == typeof(int)) { postFix = " ( int )"; } else { postFix = string.Format(" ( {0} )", method.parameterType.Name); } } methodsFormatted.Add(method.name + postFix); } int notSupportedIndex = methods.Count; int selected = methods.FindIndex(method => method.name == firstEvent.functionName); if (selected == -1) { selected = methods.Count; AnimationWindowEventMethod newMethod = new AnimationWindowEventMethod(); newMethod.name = firstEvent.functionName; newMethod.parameterType = null; methods.Add(newMethod); if (string.IsNullOrEmpty(firstEvent.functionName)) { methodsFormatted.Add(kNoneSelected); } else { methodsFormatted.Add(firstEvent.functionName + kNotSupportedPostFix); } } EditorGUIUtility.labelWidth = 130; EditorGUI.showMixedValue = !singleFunctionName; int wasSelected = singleFunctionName ? selected : -1; selected = EditorGUILayout.Popup("Function: ", selected, methodsFormatted.ToArray()); if (wasSelected != selected && selected != -1 && selected != notSupportedIndex) { foreach (var evt in data.selectedEvents) { evt.functionName = methods[selected].name; evt.stringParameter = string.Empty; } } EditorGUI.showMixedValue = false; var selectedParameter = methods[selected].parameterType; if (singleFunctionName && selectedParameter != null) { EditorGUILayout.Space(); if (selectedParameter == typeof(AnimationEvent)) { EditorGUILayout.PrefixLabel("Event Data"); } else { EditorGUILayout.PrefixLabel("Parameters"); } DoEditRegularParameters(data.selectedEvents, selectedParameter); } if (overloads.Count > 0) { EditorGUILayout.Space(); EditorGUILayout.HelpBox(s_OverloadWarning.text, MessageType.Warning, true); } } else { EditorGUI.showMixedValue = !singleFunctionName; string oldFunctionName = singleFunctionName ? firstEvent.functionName : ""; string functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), oldFunctionName).Replace(" ", ""); if (functionName != oldFunctionName) { foreach (var evt in data.selectedEvents) { evt.functionName = functionName; evt.stringParameter = string.Empty; } } EditorGUI.showMixedValue = false; if (singleFunctionName) { DoEditRegularParameters(data.selectedEvents, typeof(AnimationEvent)); } else { using (new EditorGUI.DisabledScope(true)) { AnimationEvent dummyEvent = new AnimationEvent(); DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } } if (GUI.changed) { SetData(awEvents, data); } }
public static void CollectSupportedMethods(GameObject gameObject, List <AnimationWindowEventMethod> supportedMethods, HashSet <string> overloadedMethods) { if (gameObject == null) { return; } MonoBehaviour[] behaviours = gameObject.GetComponents <MonoBehaviour>(); foreach (MonoBehaviour behaviour in behaviours) { if (behaviour == null) { continue; } Type type = behaviour.GetType(); while (type != typeof(MonoBehaviour) && type != null) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; string name = method.Name; if (!IsSupportedMethodName(name)) { continue; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length > 1) { continue; } Type parameterType = null; if (parameters.Length == 1) { parameterType = parameters[0].ParameterType; if (!(parameterType == typeof(string) || parameterType == typeof(float) || parameterType == typeof(int) || parameterType == typeof(AnimationEvent) || parameterType == typeof(UnityEngine.Object) || parameterType.IsSubclassOf(typeof(UnityEngine.Object)) || parameterType.IsEnum)) { continue; } } AnimationWindowEventMethod newMethod = new AnimationWindowEventMethod(); newMethod.name = method.Name; newMethod.parameterType = parameterType; // Since AnimationEvents only stores method name, it can't handle functions with multiple overloads. // Only retrieve first found function, but discard overloads. int existingMethodIndex = supportedMethods.FindIndex(m => m.name == name); if (existingMethodIndex != -1) { // The method is only ambiguous if it has a different signature to the one we saw before if (supportedMethods[existingMethodIndex].parameterType != parameterType) { overloadedMethods.Add(name); } } else { supportedMethods.Add(newMethod); } } type = type.BaseType; } } }