Seneral / Node_Editor_Framework

A flexible and modular Node Editor Framework for creating node based displays and editors in Unity
https://nodeeditor.seneral.dev
MIT License
2.01k stars 414 forks source link

Lots of NullReferenceException in Unity 5.2.0f3 (doesn't work) #8

Closed yuriy-universal-ivanov closed 9 years ago

yuriy-universal-ivanov commented 9 years ago

(Reverting 36ca9042d5af9b7632f93d251a3d2405b7bda2be helps)

When trying to open Window -> Node Editor:

NullReferenceException: Object reference not set to an instance of an object System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222) Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232) System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115) GUIScaleUtility.get_getTopRect () (at Assets/Plugins/Node_Editor/Framework/GUIScaleUtility.cs:16) GUIScaleUtility.BeginNoClip () (at Assets/Plugins/Node_Editor/Framework/GUIScaleUtility.cs:157) GUIScaleUtility.BeginScale (UnityEngine.Rect& rect, Vector2 zoomPivot, Single zoom, Boolean adjustGUILayout) (at Assets/Plugins/Node_Editor/Framework/GUIScaleUtility.cs:85) NodeEditorFramework.NodeEditor.DrawSubCanvas (NodeEditorFramework.NodeCanvas nodeCanvas, NodeEditorFramework.NodeEditorState editorState) (at Assets/Plugins/Node_Editor/Framework/NodeEditor.cs:140) NodeEditorFramework.NodeEditor.DrawCanvas (NodeEditorFramework.NodeCanvas nodeCanvas, NodeEditorFramework.NodeEditorState editorState) (at Assets/Plugins/Node_Editor/Framework/NodeEditor.cs:86) NodeEditorFramework.NodeEditorWindow.OnGUI () (at Assets/Editor/Node_Editor/NodeEditorWindow.cs:78) System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)

Seneral commented 9 years ago

Hi, thanks for that report. Unfortunately I could not reproduce that... Did a clean install in a new project help? What is your Unity version?

From what I can tell, the error appeared for you as a type I fetched using reflection from Unity is null. That's a very complex and weird situation, as the class UnityEngine.GUIClip is not even showing up in the Assembly browser (though searching for it results in that class existing).

I implemented it in that version to allow for grouped scaling. If you won't miss this feature, commenting out the contents of the functions BeginNoClip, MoveClipsUp and RestoreClips in GUIScalUtility should fix the error.

But I would prefer if we could sort that out:)

Seneral commented 9 years ago

Forget about the Unity version - didn't read your header too closely;) I'm using the same version, I've no idea what could cause this. Afaik reflection works on every system the same.

yuriy-universal-ivanov commented 9 years ago

Reflection works fine: GetTopRect is found (!= null), it is static and gets no arguments. The NullReferenceException is being thrown somewhere inside of the GetTopRect call, that's the point.

Brand new project, Unity 5.2.0f3, Mac OS X El Capitan, reproduced stably.

Seneral commented 9 years ago

Hm, then it's, as you said, inside the call which we have no control over. Could you try the following using GUIScaleUtility outside of the Node Editor?

Try a random reflection call, if you know about reflection Call the GUIScaleUtility.Init function. Try calling the property getTopRect, which is the source of the error above. Try calling the property getTopRectScreenSpace, which is in the same class, UnityEngine.GUIClip. If both throw the error but not the first call, it's because of the hidden class. I really have no clue why it's hidden, it's simply not accessible for you curiously. In that case, please call InspectType ("UnityEngine.GUIClip"), which is a test function I left there. Please post the console output here - there should be both above mentioned functions listed under private members.

Thanks!

Seneral commented 9 years ago

Hm InspectType seems to be broken.... Here's the code for a window:

using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
using System.Collections;

public class NodeEditorReflectionTest : EditorWindow 
{
    [MenuItem ("Window/NodeEditor Reflection Test")]
    public static void Create () 
    {
        EditorWindow.GetWindow<NodeEditorReflectionTest> ();
    }

    public void OnGUI () 
    {
        GUIScaleUtility.Init ();

        if (GUILayout.Button ("GUILayoutUtulity.s_SpaceStyle / Random Reflection Stuff"))
        {
            Assembly UnityEngine = Assembly.GetAssembly (typeof (UnityEngine.GUI));
            Type GUILayoutUtilityType = UnityEngine.GetType ("UnityEngine.GUILayoutUtility");
            FieldInfo spaceStyle = GUILayoutUtilityType.GetField ("s_SpaceStyle", BindingFlags.Static | BindingFlags.NonPublic);
            GUIStyle s_SpaceStyle = (GUIStyle)spaceStyle.GetValue (null);
            Debug.Log ("Test finished, private GUIStyle '" + s_SpaceStyle.name + "' received");
        }

        if (GUILayout.Button ("GUIClip.getTopRect / Error Problem"))
        {
            Rect topRect = GUIScaleUtility.getTopRect;
            Debug.Log ("Test finished, topRect " + topRect.ToString () + " received");
        }

        if (GUILayout.Button ("GUIClip.getTopRectScreenSpace / Potential Error Problem"))
        {
            Rect topRectScreenSpace = GUIScaleUtility.getTopRectScreenSpace;
            Debug.Log ("Test finished, topRectScreenSpace " + topRectScreenSpace.ToString () + " received");
        }

        if (GUILayout.Button ("Inspect GUIClip"))
        {
            InspectType ("UnityEngine", "UnityEngine.GUIClip");
        }
    }

    public static void InspectType (string assemblyName, string typeName) 
    {
        Assembly UnityEngine = Assembly.Load (assemblyName);
        Type type = UnityEngine.GetType (typeName);

        foreach (MemberInfo member in type.GetMembers ()) 
        {
            Debug.Log (member.MemberType.ToString () + ": " + member.ToString ());
        }
    }
}

Make sure you have Node_Editor inside the project. Can you post the results (errors) that occur when clicking any of the four buttons on the window ('Window/NodeEditor Reflection Test')?

yuriy-universal-ivanov commented 9 years ago

My guess: the problem is inside of the Unity window/clip management logic in different operating systems (GetTopRect can be required to be called in specific moments). It is always a risk that when you use undocumented functionality, it may work unexpectedly. It is even more problematic with updates (Unity will not try to keep compatibility of such stuff).

Seneral commented 9 years ago

Yes, I'll need to keep an eye on the updates to keep it supported.

I took your guess and updated the window. Just click the 'Start GUIClip.getTopRect complete test' button and cause as many event types as you can;) Each type will only be checked once. Please send me the output again:)

using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
using System.Collections.Generic;

public class NodeEditorReflectionTest : EditorWindow 
{
    [MenuItem ("Window/NodeEditor Reflection Test")]
    public static void Create () 
    {
        EditorWindow.GetWindow<NodeEditorReflectionTest> ();
    }

    public bool performTest = false;

    public List<EventType> checkedTypes = new List<EventType> ();

    public void OnGUI () 
    {
        GUIScaleUtility.Init ();

        if (GUILayout.Button ("GUILayoutUtulity.s_SpaceStyle / Random Reflection Stuff"))
        {
            Assembly UnityEngine = Assembly.GetAssembly (typeof (UnityEngine.GUI));
            Type GUILayoutUtilityType = UnityEngine.GetType ("UnityEngine.GUILayoutUtility");
            FieldInfo spaceStyle = GUILayoutUtilityType.GetField ("s_SpaceStyle", BindingFlags.Static | BindingFlags.NonPublic);
            GUIStyle s_SpaceStyle = (GUIStyle)spaceStyle.GetValue (null);
            Debug.Log ("Test finished, private GUIStyle '" + s_SpaceStyle.name + "' received");
        }

        if (GUILayout.Button ("GUIClip.getTopRect / Error Problem"))
        {
            Rect topRect = GUIScaleUtility.getTopRect;
            Debug.Log ("Test finished, topRect " + topRect.ToString () + " received");
        }

        if (GUILayout.Button ("GUIClip.getTopRectScreenSpace / Potential Error Problem"))
        {
            Rect topRectScreenSpace = GUIScaleUtility.getTopRectScreenSpace;
            Debug.Log ("Test finished, topRectScreenSpace " + topRectScreenSpace.ToString () + " received");
        }

        if (GUILayout.Button ("Inspect GUIClip"))
        {
            InspectType ("UnityEngine", "UnityEngine.GUIClip");
        }

        if (GUILayout.Button ("Start GUIClip.getTopRect complete test"))
        {
            performTest = !performTest;
            checkedTypes = new List<EventType> ();
        }

        try
        {
            if (performTest && !checkedTypes.Contains (Event.current.type))
            {
                checkedTypes.Add (Event.current.type);
                Rect topRect = GUIScaleUtility.getTopRect;
                Debug.Log ("Test in " + Event.current.type.ToString () + " suceeded! topRect " + topRect.ToString () + " received");
            }
        }
        catch (Exception e)
        {
            Debug.LogError ("Test in " + Event.current.type.ToString () + " failed! Error:" + e.ToString ());
        }
    }

    public static void InspectType (string assemblyName, string typeName) 
    {
        Assembly UnityEngine = Assembly.Load (assemblyName);
        Type type = UnityEngine.GetType (typeName);

        foreach (MemberInfo member in type.GetMembers ()) 
        {
            Debug.Log (member.MemberType.ToString () + ": " + member.ToString ());
        }
    }
}

Edit: Fixed checking events multiple times

yuriy-universal-ivanov commented 9 years ago

All failed:

Test in used failed! Error:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object at (wrapper managed-to-native) UnityEngine.GUIClip:GetTopRect () at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 --- End of inner exception stack trace --- at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 at GUIScaleUtility.get_getTopRect () [0x00000] in /Users/universal/projects/temp/tmp/Assets/Node_Editor/Framework/GUIScaleUtility.cs:16 at NodeEditorReflectionTest.OnGUI () [0x00166] in /Users/universal/projects/temp/tmp/Assets/Editor/NodeEditorReflectionTest.cs:60 UnityEngine.Debug:LogError(Object) NodeEditorReflectionTest:OnGUI() (at Assets/Editor/NodeEditorReflectionTest.cs:66) UnityEditor.DockArea:OnGUI()

Test in Layout failed! Error:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object at (wrapper managed-to-native) UnityEngine.GUIClip:GetTopRect () at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 --- End of inner exception stack trace --- at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 at GUIScaleUtility.get_getTopRect () [0x00000] in /Users/universal/projects/temp/tmp/Assets/Node_Editor/Framework/GUIScaleUtility.cs:16 at NodeEditorReflectionTest.OnGUI () [0x00166] in /Users/universal/projects/temp/tmp/Assets/Editor/NodeEditorReflectionTest.cs:60 UnityEngine.Debug:LogError(Object) NodeEditorReflectionTest:OnGUI() (at Assets/Editor/NodeEditorReflectionTest.cs:66) UnityEditor.DockArea:OnGUI()

Test in Repaint failed! Error:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object at (wrapper managed-to-native) UnityEngine.GUIClip:GetTopRect () at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 --- End of inner exception stack trace --- at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 at GUIScaleUtility.get_getTopRect () [0x00000] in /Users/universal/projects/temp/tmp/Assets/Node_Editor/Framework/GUIScaleUtility.cs:16 at NodeEditorReflectionTest.OnGUI () [0x00166] in /Users/universal/projects/temp/tmp/Assets/Editor/NodeEditorReflectionTest.cs:60 UnityEngine.Debug:LogError(Object) NodeEditorReflectionTest:OnGUI() (at Assets/Editor/NodeEditorReflectionTest.cs:66) UnityEditor.DockArea:OnGUI()

Test in mouseDrag failed! Error:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object at (wrapper managed-to-native) UnityEngine.GUIClip:GetTopRect () at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 --- End of inner exception stack trace --- at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 at GUIScaleUtility.get_getTopRect () [0x00000] in /Users/universal/projects/temp/tmp/Assets/Node_Editor/Framework/GUIScaleUtility.cs:16 at NodeEditorReflectionTest.OnGUI () [0x00166] in /Users/universal/projects/temp/tmp/Assets/Editor/NodeEditorReflectionTest.cs:60 UnityEngine.Debug:LogError(Object) NodeEditorReflectionTest:OnGUI() (at Assets/Editor/NodeEditorReflectionTest.cs:66) UnityEditor.DockArea:OnGUI()

Test in mouseUp failed! Error:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object at (wrapper managed-to-native) UnityEngine.GUIClip:GetTopRect () at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 --- End of inner exception stack trace --- at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 at GUIScaleUtility.get_getTopRect () [0x00000] in /Users/universal/projects/temp/tmp/Assets/Node_Editor/Framework/GUIScaleUtility.cs:16 at NodeEditorReflectionTest.OnGUI () [0x00166] in /Users/universal/projects/temp/tmp/Assets/Editor/NodeEditorReflectionTest.cs:60 UnityEngine.Debug:LogError(Object) NodeEditorReflectionTest:OnGUI() (at Assets/Editor/NodeEditorReflectionTest.cs:66) UnityEditor.DockArea:OnGUI()

It doesn't prove anything. Yeah, it's not available in OnGUI, but that's it. Is there any way to avoid using it?..

Seneral commented 9 years ago

Not related to Events then, either:( Sad is I can't test it myself, so figuring this out would only go with you and would probably take forever;)

I just wrote a version that allows to avoid this. Replace the region ScaleArea inside GUIScaleUtility with the following code:

    #region Scale Area

    /// <summary>
    /// Begins a scaled local area. 
    /// Returns vector to offset GUI controls with to account for zooming to the pivot. 
    /// Using adjustGUILayout does that automatically for GUILayout rects.
    /// </summary>
    public static Vector2 BeginScale (ref Rect rect, Vector2 zoomPivot, float zoom, bool adjustGUILayout) 
    {
        //GUIScaleUtility.BeginNoClip ();

        //Rect screenRect = GUIScaleUtility.InnerToScreenRect (rect);

        GUI.EndGroup ();
        Rect screenRect = rect;
#if UNITY_EDITOR
        if (!Application.isPlaying)
            screenRect.y += 23;
#endif
        // The Rect of the new clipping group to draw our nodes in
        rect = ScaleRect (screenRect, screenRect.position + zoomPivot, new Vector2 (zoom, zoom));

        // Now continue drawing using the new clipping group
        GUI.BeginGroup (rect);
        rect.position = Vector2.zero; // Adjust because we entered the new group

        // Because I currently found no way to actually scale to the center of the window rather than (0, 0),
        // I'm going to cheat and just pan it accordingly to let it appear as if it would scroll to the center
        // Note, due to that, other controls are still scaled to (0, 0)
        Vector2 zoomPosAdjust = rect.center - screenRect.size/2 + zoomPivot;

        // For GUILayout, we can make this adjustment here
        adjustedGUILayout.Add (adjustGUILayout);
        if (adjustGUILayout)
        {
            GUILayout.BeginHorizontal ();
            GUILayout.Space (rect.center.x - screenRect.size.x + zoomPivot.x);
            GUILayout.BeginVertical ();
            GUILayout.Space (rect.center.y - screenRect.size.y + zoomPivot.y);
        }

        // Take a matrix backup to restore back later on
        GUIMatrices.Add (GUI.matrix);

        // Scale GUI.matrix. After that we have the correct clipping group again.
        GUIUtility.ScaleAroundPivot (new Vector2 (1/zoom, 1/zoom), zoomPosAdjust);

        return zoomPosAdjust;
    }

    /// <summary>
    /// Ends a scale region
    /// </summary>
    public static void EndScale () 
    {
        // Set last matrix and clipping group
        if (GUIMatrices.Count == 0 || adjustedGUILayout.Count == 0)
            throw new UnityException ("GUIScaleutility: You are ending more scales than you are beginning!");
        GUI.matrix = GUIMatrices[GUIMatrices.Count-1];
        GUIMatrices.RemoveAt (GUIMatrices.Count-1);

        // End GUILayout zoomPosAdjustment
        if (adjustedGUILayout[adjustedGUILayout.Count-1])
        {
            GUILayout.EndVertical ();
            GUILayout.EndHorizontal ();
        }
        adjustedGUILayout.RemoveAt (adjustedGUILayout.Count-1);

        GUI.EndGroup ();

#if UNITY_EDITOR
        if (!Application.isPlaying)
            GUI.BeginClip (new Rect (0, 23, Screen.width, Screen.height-23));
        else
            GUI.BeginClip (new Rect (0, 0, Screen.width, Screen.height));
#else
        GUI.BeginClip (new Rect (0, 0, Screen.width, Screen.height));
#endif

//      GUIScaleUtility.RestoreClips ();
    }

    #endregion

That should make sure the three functions in Clips Hierarchy region are not called anymore. Note that you no longer can use the NodeEditor canvas in groups, though - that doesn't affect the current Editor Window nor the runtime demo, aslong as there are no top groups, everything is fine. (Runtime does NEED one top group, but no more! Or you have to delete the first EndGroup in BeginScale and the last BeginClip in EndScale).

I'll try to automate that fix for anyone getting those errors - I'll leave this issue open!

yuriy-universal-ivanov commented 9 years ago

Thank you! I'll try using it tomorrow. Making this change in a branch would be nice. Or, maybe even better, with #if.

Seneral commented 9 years ago

I'll do, after I got the approval of you that it even works :)

yuriy-universal-ivanov commented 9 years ago

@Seneral Seems to work fine with that replacement!

Seneral commented 9 years ago

Great! :)

Seneral commented 9 years ago

I the latest commit I added a compability mode which should automatically be triggered and inform the user about it.