public void TryDetectUnityEventIssues(RecordLocation location, string assetPath, GameObject target, Type componentType, string componentName, int orderIndex, SerializedProperty property) { if (!enabled) { return; } var callbacks = property.FindPropertyRelative("m_PersistentCalls.m_Calls"); if (callbacks == null || callbacks.isArray == false) { Maintainer.ConstructReportWarning("Couldn't find m_PersistentCalls in serialized UnityEvent!", IssuesFinder.ModuleName); return; } IssuesDetector.duplicateComponentDetector.ProcessProperty(callbacks); var callsCount = callbacks.arraySize; for (var i = 0; i < callsCount; i++) { var call = callbacks.GetArrayElementAtIndex(i); var callTarget = call.FindPropertyRelative("m_Target"); if (callTarget == null || callTarget.propertyType != SerializedPropertyType.ObjectReference) { Maintainer.ConstructReportWarning("Couldn't find m_Target in serialized UnityEvent's call!", IssuesFinder.ModuleName); return; } if (IsPropertyHasMissingReference(callTarget)) { AddIssue(location, assetPath, target, componentType, componentName, orderIndex, callTarget.propertyPath); return; } var callTargetObject = callTarget.objectReferenceValue; // no target set if (callTargetObject == null) { continue; } IssuesDetector.duplicateComponentDetector.ProcessProperty(callTarget); var methodName = call.FindPropertyRelative("m_MethodName"); if (methodName == null || methodName.propertyType != SerializedPropertyType.String) { Maintainer.ConstructReportWarning("Couldn't find m_MethodName in serialized UnityEvent's call!", IssuesFinder.ModuleName); return; } IssuesDetector.duplicateComponentDetector.ProcessProperty(methodName); var methodNameValue = methodName.stringValue; // no function set if (string.IsNullOrEmpty(methodNameValue)) { continue; } var arguments = call.FindPropertyRelative("m_Arguments"); if (arguments == null) { Maintainer.ConstructReportWarning("Couldn't find m_Arguments in serialized UnityEvent's call!", IssuesFinder.ModuleName); return; } var objectArgumentAssemblyTypeName = arguments.FindPropertyRelative("m_ObjectArgumentAssemblyTypeName"); if (objectArgumentAssemblyTypeName == null || objectArgumentAssemblyTypeName.propertyType != SerializedPropertyType.String) { Maintainer.ConstructReportWarning("Couldn't find m_ObjectArgumentAssemblyTypeName in m_Arguments!", IssuesFinder.ModuleName); return; } IssuesDetector.duplicateComponentDetector.ProcessProperty(objectArgumentAssemblyTypeName); var mode = call.FindPropertyRelative("m_Mode"); if (mode == null || mode.propertyType != SerializedPropertyType.Enum) { Maintainer.ConstructReportWarning("Couldn't find m_Mode in serialized UnityEvent's call!", IssuesFinder.ModuleName); return; } IssuesDetector.duplicateComponentDetector.ProcessProperty(mode); var modeValue = (PersistentListenerMode)mode.enumValueIndex; var dummyEvent = CSReflectionTools.GetDummyEvent(property); if (dummyEvent == null) { Maintainer.ConstructReportWarning("Couldn't get something from GetDummyEvent!", IssuesFinder.ModuleName); return; } var type = CSReflectionTools.objectType; var stringValue = objectArgumentAssemblyTypeName.stringValue; if (!string.IsNullOrEmpty(stringValue)) { type = Type.GetType(stringValue, false) ?? typeof(UnityEngine.Object); } if (!UnityEventDrawer.IsPersistantListenerValid(dummyEvent, methodNameValue, callTargetObject, modeValue, type)) { AddIssue(location, assetPath, target, componentType, componentName, orderIndex, methodName.propertyPath); return; } } }
private bool isUnityEventsNullOrMissing(MonoBehaviour monoBehaviour, bool printError) { targetPropertiesNames.Clear(); FieldInfo[] fieldArray = monoBehaviour.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int i = fieldArray.Length - 1; i >= 0; i--) { FieldInfo field = fieldArray[i]; if (field.FieldType == typeof(UnityEventBase) || field.FieldType.IsSubclassOf(typeof(UnityEventBase))) { targetPropertiesNames.Add(field.Name); } } if (targetPropertiesNames.Count > 0) { SerializedObject serializedMonoBehaviour = new SerializedObject(monoBehaviour); for (int i = targetPropertiesNames.Count - 1; i >= 0; i--) { string targetProperty = targetPropertiesNames[i]; SerializedProperty property = serializedMonoBehaviour.FindProperty(targetProperty); SerializedProperty propertyRelativeArrray = property.FindPropertyRelative("m_PersistentCalls.m_Calls"); for (int j = propertyRelativeArrray.arraySize - 1; j >= 0; j--) { SerializedProperty arrayElementAtIndex = propertyRelativeArrray.GetArrayElementAtIndex(j); SerializedProperty propertyTarget = arrayElementAtIndex.FindPropertyRelative("m_Target"); if (propertyTarget.objectReferenceValue == null) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event object reference is null"); } else { return(true); } } SerializedProperty propertyMethodName = arrayElementAtIndex.FindPropertyRelative("m_MethodName"); if (string.IsNullOrEmpty(propertyMethodName.stringValue)) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event handler function is not selected"); continue; } else { return(true); } } string argumentAssemblyTypeName = arrayElementAtIndex.FindPropertyRelative("m_Arguments").FindPropertyRelative("m_ObjectArgumentAssemblyTypeName").stringValue; System.Type argumentAssemblyType; if (!string.IsNullOrEmpty(argumentAssemblyTypeName)) { argumentAssemblyType = System.Type.GetType(argumentAssemblyTypeName, false) ?? typeof(UnityEngine.Object); } else { argumentAssemblyType = typeof(UnityEngine.Object); } UnityEventBase dummyEvent; System.Type propertyTypeName = System.Type.GetType(property.FindPropertyRelative("m_TypeName").stringValue, false); if (propertyTypeName == null) { dummyEvent = (UnityEventBase) new UnityEvent(); } else { dummyEvent = Activator.CreateInstance(propertyTypeName) as UnityEventBase; } if (!UnityEventDrawer.IsPersistantListenerValid(dummyEvent, propertyMethodName.stringValue, propertyTarget.objectReferenceValue, (PersistentListenerMode)arrayElementAtIndex.FindPropertyRelative("m_Mode").enumValueIndex, argumentAssemblyType)) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event handler function is missing"); } else { return(true); } } } } } return(false); }
protected virtual void DrawEvent(Rect rect, int index, bool isActive, bool isFocused) { var pListener = m_ListenersArray.GetArrayElementAtIndex(index); var contentRect = rect; contentRect.xMin -= 6; contentRect.xMax += 2; contentRect.y += 1; Rect[] subRects = GetRowRects(contentRect); Rect enabledRect = subRects[0]; Rect goRect = subRects[1]; Rect functionRect = subRects[2]; Rect argRect = subRects[3]; // find the current event target... var callState = pListener.FindPropertyRelative(kCallStatePath); var mode = pListener.FindPropertyRelative(kModePath); var arguments = pListener.FindPropertyRelative(kArgumentsPath); var listenerTarget = pListener.FindPropertyRelative(kInstancePath); var methodName = pListener.FindPropertyRelative(kMethodNamePath); Color c = GUI.backgroundColor; GUI.backgroundColor = Color.white; var callStateEnum = (UnityEventCallState)callState.enumValueIndex; var isEditorAndRuntime = callStateEnum == UnityEventCallState.EditorAndRuntime; var isRuntime = callStateEnum == UnityEventCallState.RuntimeOnly; var toggleRect = enabledRect; toggleRect.width = 16; if (isEditorAndRuntime || (isRuntime && Application.isPlaying)) { var markRect = new Rect(rect) { width = 2 }; markRect.x -= 20; EditorGUI.DrawRect(markRect, new Color(1, 0.7f, 0.4f, 1)); } var evt = Event.current; var color = GUI.color; var mousePos = evt.mousePosition; { var isHover = toggleRect.Contains(mousePos); if (isHover) { // Ooh, these beautiful 2-pixels of rounded edges.. GUI.DrawTexture(toggleRect, Texture2D.whiteTexture, ScaleMode.ScaleToFit, true, 1, new Color(1, 1, 1, 0.15f), Vector4.zero, 2); } } GUI.color = new Color(1, 1, 1, 0.75f); GUI.Box(toggleRect, DropdownIcon, EditorStyles.centeredGreyMiniLabel); GUI.color = color; GUI.color = new Color(0, 0, 0, 0); EditorGUI.PropertyField(toggleRect, callState, GUIContent.none); GUI.color = color; var isOff = callStateEnum == UnityEventCallState.Off; EditorGUI.BeginDisabledGroup(isOff); EditorGUI.BeginChangeCheck(); { GUI.Box(goRect, GUIContent.none); EditorGUI.PropertyField(goRect, listenerTarget, GUIContent.none); if (EditorGUI.EndChangeCheck()) { methodName.stringValue = null; } } SerializedProperty argument; var modeEnum = GetMode(mode); //only allow argument if we have a valid target / method if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue)) { modeEnum = PersistentListenerMode.Void; } switch (modeEnum) { case PersistentListenerMode.Float: argument = arguments.FindPropertyRelative(kFloatArgument); break; case PersistentListenerMode.Int: argument = arguments.FindPropertyRelative(kIntArgument); break; case PersistentListenerMode.Object: argument = arguments.FindPropertyRelative(kObjectArgument); break; case PersistentListenerMode.String: argument = arguments.FindPropertyRelative(kStringArgument); break; case PersistentListenerMode.Bool: argument = arguments.FindPropertyRelative(kBoolArgument); break; default: argument = arguments.FindPropertyRelative(kIntArgument); break; } var desiredArgTypeName = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName).stringValue; var desiredType = typeof(Object); if (!string.IsNullOrEmpty(desiredArgTypeName)) { desiredType = Type.GetType(desiredArgTypeName, false) ?? typeof(Object); } argRect.xMin = goRect.xMax + Spacing; if (modeEnum == PersistentListenerMode.Object) { EditorGUI.BeginChangeCheck(); var result = EditorGUI.ObjectField(argRect, GUIContent.none, argument.objectReferenceValue, desiredType, true); if (EditorGUI.EndChangeCheck()) { argument.objectReferenceValue = result; } } else if (modeEnum != PersistentListenerMode.Void && modeEnum != PersistentListenerMode.EventDefined) { EditorGUI.PropertyField(argRect, argument, GUIContent.none); } using (new EditorGUI.DisabledScope(listenerTarget.objectReferenceValue == null)) { EditorGUI.BeginProperty(functionRect, GUIContent.none, methodName); { GUIContent buttonContent; if (EditorGUI.showMixedValue) { buttonContent = MixedValueContent; } else { var buttonLabel = new StringBuilder(); if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue)) { buttonLabel.Append(kNoFunctionString); } else if (!UnityEventDrawer.IsPersistantListenerValid(m_DummyEvent, methodName.stringValue, listenerTarget.objectReferenceValue, GetMode(mode), desiredType)) { var instanceString = "UnknownComponent"; var instance = listenerTarget.objectReferenceValue; if (instance != null) { instanceString = instance.GetType().Name; } buttonLabel.Append(string.Format("<Missing {0}.{1}>", instanceString, methodName.stringValue)); } else { buttonLabel.Append(listenerTarget.objectReferenceValue.GetType().Name); if (!string.IsNullOrEmpty(methodName.stringValue)) { buttonLabel.Append("."); if (methodName.stringValue.StartsWith("set_")) { buttonLabel.Append(methodName.stringValue.Substring(4)); } else { buttonLabel.Append(methodName.stringValue); } } } TempContent.text = buttonLabel.ToString(); buttonContent = TempContent; } if (GUI.Button(functionRect, buttonContent, EditorStyles.popup)) { var popup = BuildPopupList.Invoke(null, new object[] { listenerTarget.objectReferenceValue, m_DummyEvent, pListener }) as GenericMenu; popup.DropDown(functionRect); } } EditorGUI.EndProperty(); } EditorGUI.EndDisabledGroup(); GUI.backgroundColor = c; }