Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Simple node editor

Discussion in 'Immediate Mode GUI (IMGUI)' started by unimechanic, Jul 5, 2013.

  1. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    Hey guys

    I'm trying to get a GUILayout.Button to show/hide another GUILayout.Button on a node, but I can't figure out how.

    Anyone know how to do that?

    Code (CSharp):
    1.  GUILayout.Space(5);
    2.             if (GUILayout.Button(GoalActive ? "IsGOAL" : "IsNotGOAL"))
    3.                 GoalActive = !GoalActive;
    4.            
    5.             GUILayout.Space(5);
    6.             if (GUILayout.Button(maxMinSwitch ? "MAX" : "MIN"))
    7.                 maxMinSwitch = !maxMinSwitch;
    GoalActive needs to show/hide maxMinSwitch.
     
  2. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    287
    @Jamsa78 I think you just need to wrap another if statement around the 2nd button's if statement, like this:

    Code (CSharp):
    1. GUILayout.Space(5);
    2. if (GUILayout.Button(GoalActive ? "IsGOAL" : "IsNotGOAL"))
    3.     GoalActive = !GoalActive;
    4.  
    5. GUILayout.Space(5);
    6. if (GoalActive) {
    7.     if (GUILayout.Button(maxMinSwitch ? "MAX" : "MIN"))
    8.         maxMinSwitch = !maxMinSwitch;
    9. }
    10.  
    Therefore, if GoalActive (in the example code here) is false, then the GUILayout.Button(...) code will not run at all, and thus, no button will be drawn.
    When GoalActive turns true, then it will show the button.
     
    Jamsa78 likes this.
  3. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    Heh, you know...sometime it's just the simple stuff you overlook. :D

    Thanks, it's works perfectly! :)
     
    ModLunar likes this.
  4. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    And now that I have that working, I would like to change the GoalActive to a standard "boring" checkbox.

    I've tried using a toggle, but being a mere casual unity dev, the standard example is useless. And I don't even know if that's the right one to use. :D
     
  5. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    287
    @Jamsa78 Hahah I know right? You're welcome, glad to help!

    Oh, what's wrong with using EditorGUILayout.Toggle(...)?
     
    Jamsa78 likes this.
  6. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    ...not working runtime? :) Sorry, didn't mention that little detail. :D
     
  7. StephenL

    StephenL

    Joined:
    Oct 18, 2010
    Posts:
    218
    GUILayout.Toggle()
     
  8. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    Yeah...as I said I couldn't get it to work.

    If I'm not getting compile error, the program hangs when adding a node with the toggle. :(

    But maybe you've got an example that works? :)
     
  9. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    ...and just like that, it works today.

    I swear, my pc is the weirdest pc ever. :D
     
  10. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    88
    Hey guys

    I'm trying to implement a logging function to log when the user:
    • Adds a new node (ID(=the node name) and location)
    • Deletes a node(type and id)
    • Moves a node(id and to/from position)
    • Creates a connection(from and to (node id and knob name) )
    • Deletes a connection(from and to (node id and knob name) )
    • Changes to node properties( like name, value, toggle etc)
    • Saves/loads a canvas (filename)
    Anybody got idea as to do this a sensible way? :D

    The default log is "contaminated" by unity logentries, so that can't used. It has to be a separate, dedicated log. :)

    And yes, this is also something that has to work runtime. ;)
     
  11. pnf4665jds

    pnf4665jds

    Joined:
    Jun 4, 2020
    Posts:
    1
    Hi, I am trying to make a node that display the executing time at run time.

    But, the problem is that the showed time will not increase and the value depends on when I open the canvas.

    Is there any way to make a dynamic display at run time?
     
  12. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,205
    Sorry for the late reply. Just like any other window the Node Editor Window is usually only drawn when something changes. I guess you can call the repaint function of the editor window (found using getwindow) to ensure it keeps being drawn. You can either call it each time such a node is drawn to ensure fastest update rate or call it in a separate update loop at your own pace.
    There are several events you can subscribe to; all but change to node properties are supported. That one is a bit tricky, but I guess you could add a GUI.changed check after each NodeGUI to get this - however you'll not know WHAT exactly changed unless you do a SerializedObject comparision or manually whip up some reflection logic.
     
  13. Achondrite

    Achondrite

    Joined:
    Sep 23, 2015
    Posts:
    8
    I'm having issues sorting the rendered NEF nodes in IMGUI with unity other canvases.
    I thought rendering the output of NEF to a render target would be the way to go.

    My setup holds a few regions and subwindows with one on the bottom containing the NEF.
    Rendering to a render target works but the nodes are not rendered on the same position as without rendering to a rendertexture.
    It seems like the nodes are rendered too much (the y-offset of my subwindow containing NEF to the top of the application window) to the bottom.
    I've been trying to play with the canvasRect and other parameters but with no success so far.

    Does anyone have some pointers on how to get this right?

    Thanks
     
  14. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,205
    Rendering to a render target sounds like a workaround to me, not a solution.
    Haven't worked too much with GUI at runtime, but GUI.depth sounds like something to look into.
    Try to set it in the RTNodeEditor script before rendering and reset it afterwards.
     
  15. MegaMasterX

    MegaMasterX

    Joined:
    Apr 6, 2014
    Posts:
    9
    Hey there. Got the LTS packages installed (per documentation) and implemented some basic nodes but the Node Editor window is flat out not showing up when going to Window > Node Editor. There aren't any errors being thrown either; Hopefully I'm just missing something simple. Thoughts?
     
  16. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,205
    Latest version I have currently installed is 2019.4, but I've tested 2020 before too, and no problems. Added using package manager with the git urls and it's opening just fine. Any exact unity version? Does this happen in an empty project as well?
    The window, at the very least, should always show, even if something in the setup failed. That makes me think maybe your layout is messed up - maybe try Window/Layout/Revert?
     
    Last edited: Jan 14, 2021
    MegaMasterX likes this.
  17. MegaMasterX

    MegaMasterX

    Joined:
    Apr 6, 2014
    Posts:
    9
    I'm currently using 2020.2.0f1.

    It looks like resetting the layout worked. Thank you!
     
  18. GintarasOE

    GintarasOE

    Joined:
    Sep 17, 2019
    Posts:
    2
    Hi, so a lot of stuff in this thread - maybe I just missed something but: is there any way to get something like nodes tree? Some representation of what nodes are used and how are they connected?

    Basically, I'm working on some code generation tool, the idea is: user adds and connects nodes in the way he wants and the system outputs generated script. I want to have one node responsible for file generation let's call it "OutputNode" - this node needs to get some representation of all nodes in canvas it is in and how each of them connects to properly resolve what code to put in the output file.

    Is there any way to get this info?
     
  19. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,205
    Sure, I'll quickly go over two parts to this.

    First, structure.
    Make a custom canvas, where you can decide whether you want to allow loops or not.
    Then decide where you want to put the generation code. If you only want one Output per graph, I recommend you to not have the code in the output node itself (although you can) but in a traversal routine (check examples, especially the standard calculation). Then read some setup parameters from the output node. Then it's just a matter of going through the node structure (see the traversal, some runtime examples, or FlowNode for that). You have the responsibility in your custom nodes to call something like
    Code (CSharp):
    1. NodeEditor.curNodeCanvas.OnNodeChange (this);
    to notify your traversal routine that a node updated.
    You can enforce only one output node (and have it appear by default), see graph canvas example for a similar example.

    Second, visuals.
    FlowNode has some examples of explicit knob placement, you can also put them on top to get a vertical graph, etc. Unfortunately there's no toggle to switch to straight lines so if that's the visual you're going for, you can add an overload here to this function).
     
  20. GintarasOE

    GintarasOE

    Joined:
    Sep 17, 2019
    Posts:
    2
    Thank you! It seems that slowly I'm starting to get where I want.
     
  21. SpawnOfLinkTIS

    SpawnOfLinkTIS

    Joined:
    Jul 3, 2013
    Posts:
    5
    I'd like to create nodes that can link to Scriptable Objects. Those SOs would also contain editable data, and the ability to draw that data.
    Ultimately, lists of Animations, lists of strings, lists of instantiated objects is the goal. This fails, and I'd like to understand why. I thought it was due to 'GetScriptableObjects', but that hasn't improved the situation. A string should be serializable.

    Node Class:
    Code (CSharp):
    1. using System;
    2. using NodeEditorFramework;
    3. using UnityEngine;
    4.  
    5. using UnityEditor;
    6. [Node(false, "Schedule/Word Test Node", new Type[] { typeof(NPCScheduleCanvas) })]
    7. public class BasicNestedNode : Node
    8. {
    9.     public override string Title { get { return "Word Test Node"; } }
    10.     public override Vector2 MinSize { get { return new Vector2(350, 60); } }
    11.     public override bool AutoLayout { get { return true; } }
    12.     private const string Id = "BasicNestedNode";
    13.     public override string GetID { get { return Id; } }
    14.     public Type GetObjectType { get { return typeof(BasicNestedNode); } }
    15.  
    16.     BasicSO_WithDraw myWordsSO;
    17.     public string someText;
    18.     protected override void OnCreate()
    19.     {
    20.         myWordsSO = ScriptableObject.CreateInstance<BasicSO_WithDraw>();
    21.     }
    22.  
    23.     public override void NodeGUI()
    24.     {
    25.         EditorGUILayout.BeginHorizontal();
    26.         someText = EditorGUILayout.TextField("Some Text Box", someText);
    27.         EditorGUILayout.EndHorizontal();
    28.      
    29.         EditorGUILayout.BeginHorizontal();
    30.         myWordsSO.DrawStringBox();
    31.         EditorGUILayout.EndHorizontal();
    32.     }
    33.     public override ScriptableObject[] GetScriptableObjects()
    34.     {
    35.         return new ScriptableObject[] {myWordsSO};
    36.     }
    37.  
    38.     protected override void CopyScriptableObjects(Func<ScriptableObject, ScriptableObject> replaceSO)
    39.     {
    40.         myWordsSO = replaceSO(myWordsSO) as BasicSO_WithDraw;
    41.     }
    42.  
    43. }
    44.  
    BasicSO_WithDraw:
    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [Serializable]
    6. public class BasicSO_WithDraw : ScriptableObject
    7. {
    8.     [SerializeField]
    9.     public string someWords;
    10.     public BasicSO_WithDraw()
    11.     {
    12.      
    13.     }
    14.     public void DrawStringBox()
    15.     {
    16.         GUILayout.BeginHorizontal();
    17.         someWords = EditorGUILayout.TextField("Words?", someWords);
    18.         GUILayout.EndHorizontal();
    19.     }
    20. }
    Error message occurs on a Reload or Play
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. NodeEditorFramework.NodeEditorSaveManager.Clone[T] (T SO) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Framework/SaveSystem/NodeEditorSaveManager.cs:518)
    3. NodeEditorFramework.NodeEditorSaveManager.AddClonedSOs[T] (System.Collections.Generic.List`1[T] scriptableObjects, System.Collections.Generic.List`1[T] clonedScriptableObjects, System.Collections.Generic.ICollection`1[T] initialSOs) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Framework/SaveSystem/NodeEditorSaveManager.cs:534)
    4. NodeEditorFramework.NodeEditorSaveManager.CreateWorkingCopy (NodeEditorFramework.NodeCanvas nodeCanvas, System.Boolean editorStates) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Framework/SaveSystem/NodeEditorSaveManager.cs:435)
    5. NodeEditorFramework.NodeEditorSaveManager.LoadNodeCanvas (System.String path, System.Boolean createWorkingCopy) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Framework/SaveSystem/NodeEditorSaveManager.cs:348)
    6. NodeEditorFramework.NodeEditorUserCache.LoadNodeCanvas (System.String path) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Framework/SaveSystem/NodeEditorUserCache.cs:394)
    7. NodeEditorFramework.Standard.NodeEditorInterface.ReloadCanvas () (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Standard/Interface/NodeEditorInterface.cs:188)
    8. NodeEditorFramework.Utilities.PopupMenu+MenuItem.Execute () (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Utilities/GUI/OverlayGUI.cs:360)
    9. NodeEditorFramework.Utilities.PopupMenu.DrawItem (NodeEditorFramework.Utilities.PopupMenu+MenuItem item, UnityEngine.Rect groupRect) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Utilities/GUI/OverlayGUI.cs:265)
    10. NodeEditorFramework.Utilities.PopupMenu.DrawGroup (UnityEngine.Rect pos, System.Collections.Generic.List`1[T] menuItems) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Utilities/GUI/OverlayGUI.cs:217)
    11. NodeEditorFramework.Utilities.PopupMenu.Draw () (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Utilities/GUI/OverlayGUI.cs:192)
    12. NodeEditorFramework.Utilities.OverlayGUI.StartOverlayGUI (System.String editorUser) (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Runtime/Utilities/GUI/OverlayGUI.cs:39)
    13. NodeEditorFramework.Standard.NodeEditorWindow.OnGUI () (at Library/PackageCache/com.seneral.nodeeditorframework@768b0e66dc/Editor/NodeEditorWindow.cs:251)
     

    Attached Files:

  22. SpawnOfLinkTIS

    SpawnOfLinkTIS

    Joined:
    Jul 3, 2013
    Posts:
    5
    After some additional testing, I think the issue is that in NodeEditorSaveManager #425 it overrides the existing nodeCanvas with the cloned copy. Since this copy isn't a deep copy, the newly instantiated SO is not copied. Nor is the reference available for the cloned version in GetScriptableObjects().
    Code (CSharp):
    1.             nodeCanvas = AddClonedSO (allSOs, clonedSOs, nodeCanvas);
    2.             AddClonedSOs (allSOs, clonedSOs, nodeCanvas.GetScriptableObjects ());
    I tried performing an AssetDatabase.CreateAsset and SaveAssets in the OnCreate() as well. No dice.
    Code (CSharp):
    1.         assetName = UnityEditor.AssetDatabase.GenerateUniqueAssetPath("Assets/NewScripableObject.asset");
    2.         AssetDatabase.CreateAsset(myBasicSO, assetName);
    3.         AssetDatabase.SaveAssets();
    It seems one work around would be to have a static asset name and using "
    AssetDatabase.LoadAssetAtPath" within the GetScriptableObjects(), but this seems very ugly.

    Still interested in any feedback you may have.
     
  23. Nihil688

    Nihil688

    Joined:
    Mar 12, 2013
    Posts:
    462
    Has anyone managed to make a live view / highlight of path? I want to create a similar to mecanim window where you can view the selected objects current selected path. AlsoI wouldn't use it to load/save a path, I would instead generate it on the fly as well
     
    ModLunar likes this.
  24. Nihil688

    Nihil688

    Joined:
    Mar 12, 2013
    Posts:
    462
    I actually managed it myself in the end with small changes from the original. I've removed caching and managed to reuse the canvas without reloading it each time as it took ages and generally after those two improvements it all works nicely now :)

    I just need to create the window placement in a nice way because in a very complex system one will fall on the other but other than "UI" everything works ok!

    GJ to the original makers, I am so happy I didn't have to write all that myself even though I was a bit skeptic at first
     
  25. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,205
    Yeah sorry I'm not very active on here anymore, haven't really used unity for some years now and I'm currently busy with a thesis. So you're right to be sceptical, I cannot guarantee support going forward.
    Glad you got it to work though.
    If I remember right the caching is a big part to enable a reliable workflow that people expect, e.g. one where nodes and changes do not dissappear as you do things (load scenes, enter playmode, etc.). Unfortunately that means it has to serialize all ScriptableObject again each time, and Unity is very slow at that. Since Unity provides (at least last time I checked years ago) no callbacks to save stuff before a scene switch, the only reliable way to do this is to save the graph to a working copy on disk every time you click out of the window.
    Trust me when I say I tried making it ok, at some point I had an attempt at an incremental save system that build that working copy as you worked, but that was more complex and I removed it later.
    In addition, since this was originally one of my very first coding projects, code structure / design might suffer in places, though I have worked on ironing most of that out over the years. It also uses quite a bit of reflection to make code integration easier and of course has a runtime GUI as well, which are both rather nice but for simpler use cases it is not the smallest framework...
     
    manpower13 and Nihil688 like this.
  26. Nihil688

    Nihil688

    Joined:
    Mar 12, 2013
    Posts:
    462
    For me it was creating my own vs learning someone else's to do what I wanted but you had features that would take me ages no matter what. I was just hoping others were here to reply too. I usually post my question then go work at it whilst waiting for an answer so things happen simultaneously :)
    Some things are pretty tangled but if this was your first project then you sticking through it is what matters as the result is very good. I would never complain about an open source project that I decide to use cause I would either rework it and thus it's up to me to maintain it afterwards or I would just never use it.
     
    Seneral likes this.
  27. SpawnOfLinkTIS

    SpawnOfLinkTIS

    Joined:
    Jul 3, 2013
    Posts:
    5
    To wrap up my previous post.
    I ended up refactoring the entire node structure and inheritance. Instead of using C# Interfaces and Scriptable Objects to draw and store the data, I now have one massive configurable node. I effectively swapped type checking of generic nodes with boolean checking of the node configuration. Certainly not as elegant.
    I simply could not get the framework to support data storage in non-Node (class inheritance) objects. Even writing my own lateral data store, which would hold Lists and references to newly instantiated sub classes failed once the Node framework refreshed.

    If someone were to take up this project, I suggest they look into purchasing and learning from Pixel Crushers' Dialog System. It is a very complex node system with auto saving. Someone with more time, and more expertise can probably get what they need out of that framework to make this one even better.
     
unityunity