Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Simple node editor

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

  1. unimechanic

    unimechanic

    Joined:
    Jan 9, 2013
    Posts:
    155
    Here is the example code to create a node editor, in which you have draggable windows connected by a curve:

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. public class NodeEditor: EditorWindow {
    5.    
    6.     Rect window1;
    7.     Rect window2;
    8.    
    9.     [MenuItem("Window/Node editor")]
    10.     static void ShowEditor() {
    11.         NodeEditor editor = EditorWindow.GetWindow<NodeEditor>();
    12.         editor.Init();
    13.     }
    14.    
    15.     public void Init() {
    16.         window1 = new Rect(10, 10, 100, 100);  
    17.         window2 = new Rect(210, 210, 100, 100);
    18.     }
    19.    
    20.     void OnGUI() {
    21.         DrawNodeCurve(window1, window2); // Here the curve is drawn under the windows
    22.        
    23.         BeginWindows();
    24.         window1 = GUI.Window(1, window1, DrawNodeWindow, "Window 1");   // Updates the Rect's when these are dragged
    25.         window2 = GUI.Window(2, window2, DrawNodeWindow, "Window 2");
    26.         EndWindows();
    27.     }
    28.    
    29.     void DrawNodeWindow(int id) {
    30.         GUI.DragWindow();
    31.     }
    32.    
    33.     void DrawNodeCurve(Rect start, Rect end) {
    34.         Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0);
    35.         Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0);
    36.         Vector3 startTan = startPos + Vector3.right * 50;
    37.         Vector3 endTan = endPos + Vector3.left * 50;
    38.         Color shadowCol = new Color(0, 0, 0, 0.06f);
    39.         for (int i = 0; i < 3; i++) // Draw a shadow
    40.             Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5);
    41.         Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 1);
    42.     }
    43. }
    44.  
    Save it as "NodeEditor.cs" in your Assets/Editor folder.

    UPDATE: An image of the editor extension:

    $NodeEditor.png
     
    Last edited: Jul 9, 2013
  2. the7347

    the7347

    Joined:
    Dec 29, 2012
    Posts:
    33
    Ufff ... not imagine how much I wanted this
    thanks for posting :)
     
  3. tatelax

    tatelax

    Joined:
    Feb 4, 2010
    Posts:
    1,168
    I've modified this script a bit, hopefully it will be more useful.

    You can now press a button to create a new node, then you can click a button to attach nodes together.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections.Generic;
    5.  
    6.  
    7. public class NodeEditor : EditorWindow {
    8.  
    9.     List<Rect> windows = new List<Rect>();
    10.     List<int> windowsToAttach = new List<int>();
    11.     List<int> attachedWindows = new List<int>();
    12.  
    13.     [MenuItem("Window/Node editor")]
    14.     static void ShowEditor() {
    15.         NodeEditor editor = EditorWindow.GetWindow<NodeEditor>();
    16.     }
    17.  
    18.  
    19.     void OnGUI() {
    20.         if (windowsToAttach.Count == 2) {
    21.             attachedWindows.Add(windowsToAttach[0]);
    22.             attachedWindows.Add(windowsToAttach[1]);
    23.             windowsToAttach = new List<int>();
    24.         }
    25.  
    26.         if (attachedWindows.Count >= 2) {
    27.             for (int i = 0; i < attachedWindows.Count; i += 2) {
    28.                 DrawNodeCurve(windows[attachedWindows[i]], windows[attachedWindows[i + 1]]);
    29.             }
    30.         }
    31.  
    32.         BeginWindows();
    33.  
    34.         if (GUILayout.Button("Create Node")) {
    35.             windows.Add(new Rect(10, 10, 100, 100));
    36.         }
    37.  
    38.         for (int i = 0; i < windows.Count; i++) {
    39.             windows[i] = GUI.Window(i, windows[i], DrawNodeWindow, "Window " + i);
    40.         }
    41.  
    42.         EndWindows();
    43.     }
    44.  
    45.  
    46.     void DrawNodeWindow(int id) {
    47.         if (GUILayout.Button("Attach")) {
    48.             windowsToAttach.Add(id);
    49.         }
    50.  
    51.         GUI.DragWindow();
    52.     }
    53.  
    54.  
    55.     void DrawNodeCurve(Rect start, Rect end) {
    56.         Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0);
    57.         Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0);
    58.         Vector3 startTan = startPos + Vector3.right * 50;
    59.         Vector3 endTan = endPos + Vector3.left * 50;
    60.         Color shadowCol = new Color(0, 0, 0, 0.06f);
    61.  
    62.         for (int i = 0; i < 3; i++) {// Draw a shadow
    63.             Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5);
    64.         }
    65.  
    66.         Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 1);
    67.     }
    68. }
    69.  
     
    Last edited: Jul 9, 2013
  4. unimechanic

    unimechanic

    Joined:
    Jan 9, 2013
    Posts:
    155
    Very nice, tatelax. Thanks for contributing :)
     
  5. krides

    krides

    Joined:
    Apr 7, 2011
    Posts:
    38
    This is really nice, I am going to use this instead of Twine for my branching story/dialogue trees. Thank you guys!
     
  6. Venged

    Venged

    Joined:
    Oct 24, 2010
    Posts:
    500
    Thanks for this. I have been wanting to learn about this. How can I destroy a node or attachment? Where is good place to start if i want to learn how to add scripts, variables, or some type of functionality to the nodes? If there a resource I can read?

    Thanks!
     
  7. marcelobr

    marcelobr

    Joined:
    Sep 10, 2012
    Posts:
    12
    can someone tell me
    where can i learn more about program in this node?
     
  8. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    Exactly what I was looking for! Thank you tatelax.

    Question, how difficult would it be to add alt+mouse drag to scroll the area inside the editor window to add and view more windows?

    Thanks

    -Raiden
     
  9. EMOTION-THEORY

    EMOTION-THEORY

    Joined:
    Jul 16, 2013
    Posts:
    83
    Hey this is awesome! Thanks guys.

    Question: If I wanted to have each Window display like an Inspector for a certain class object, how would I do that (if it's even possible) ?

    Suppose I have a Serializable class DialogNode.cs

    Code (csharp):
    1.  
    2. public int id;
    3. public string name;
    4. public string phrase;
    5.  
    We all know how this would appear in the Inspector... would it be possible to get the same thing, only displaying in each of those Windows?

    Thanks :D
     
  10. MrMatthias

    MrMatthias

    Joined:
    Sep 18, 2012
    Posts:
    191
    You'd have to draw them yourself. You can get the variables and their types via Reflection. I'm working on something like that.
     
  11. unimechanic

    unimechanic

    Joined:
    Jan 9, 2013
    Posts:
    155
    If you ever need panning the window, you can group controls using this method:

    http://docs.unity3d.com/Documentation/ScriptReference/GUI.BeginGroup.html

    And move the group's coordinates. The first code example can be modified this way:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. public class NodeEditor: EditorWindow {        
    6.     Rect window1;  
    7.     Rect window2;
    8.     float panX = 0;
    9.     float panY = 0;
    10.        
    11.     [MenuItem("Window/Node editor")]   
    12.     static void ShowEditor() {     
    13.         NodeEditor editor = EditorWindow.GetWindow<NodeEditor>();      
    14.         editor.Init();     
    15.     }      
    16.    
    17.     public void Init() {       
    18.         window1 = new Rect(100, 100, 100, 100);        
    19.         window2 = new Rect(260, 260, 100, 100);        
    20.     }
    21.  
    22.     void OnGUI() {
    23.         GUI.BeginGroup(new Rect(panX, panY, 100000, 100000));
    24.         DrawNodeCurve(window1, window2); // Here the curve is drawn under the windows              
    25.        
    26.         BeginWindows();    
    27.         window1 = GUI.Window(1, window1, DrawNodeWindow, "Window 1");   // Updates the Rect's when these are dragged       
    28.         window2 = GUI.Window(2, window2, DrawNodeWindow, "Window 2");      
    29.         EndWindows();
    30.  
    31.         GUI.EndGroup();    
    32.  
    33.         if (GUI.RepeatButton(new Rect(15, 5, 20, 20), "^")) {
    34.             panY -= 1;
    35.             Repaint();
    36.         }
    37.        
    38.         if (GUI.RepeatButton(new Rect(5, 25, 20, 20), "<")) {
    39.             panX -= 1;
    40.             Repaint();
    41.         }
    42.        
    43.         if (GUI.RepeatButton(new Rect(25, 25, 20, 20), ">")) {
    44.             panX += 1;
    45.             Repaint();
    46.         }
    47.        
    48.         if (GUI.RepeatButton(new Rect(15, 45, 20, 20), "v")) {
    49.             panY += 1;
    50.             Repaint();
    51.         }
    52.     }
    53.            
    54.     void DrawNodeWindow(int id) {      
    55.         GUI.DragWindow();      
    56.     }
    57.  
    58.     void DrawNodeCurve(Rect start, Rect end) {
    59.         Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0);      
    60.         Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0);    
    61.         Vector3 startTan = startPos + Vector3.right * 50;      
    62.         Vector3 endTan = endPos + Vector3.left * 50;       
    63.         Color shadowCol = new Color(0, 0, 0, 0.06f);       
    64.         for (int i = 0; i < 3; i++) // Draw a shadow           
    65.             Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5);      
    66.         Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 1);      
    67.     }  
    68. }
     
  12. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    Hello,

    I've added a couple things.. mostly the ability to "resize" the node window. Ideally the node stuff should probably be in a class, but it mostly works and is an example right? :)

    One issue I tried to resolve was the rubber banding if you move your mouse to quickly while the handle is active and you're dragging to resize... oh, and the fact that once you reach the minimum size (_winMinY, _winMinX), their is a bit of snapping... if someone can help me fix that, it would be awesome (perhaps its' just a matter of getting the delta "acceleration" of the mouse movement.. hint, hint).

    anyway, just wanted to give back a little as this example and others from Bunny83 drove me to this solution.. (note: the "free-to-use" corner image I made is attached here, just put it in the right folder and change the asset load path as you like (NodeEditor/Icons) )

    enjoy!




    Code (CSharp):
    1.  
    2.  
    3. //use code in followup
    4.  
    5.  

    (edit).. also note, the image asset when imported should be configured with advanced, non-power-of-2 = none, alpha is transparency enabled, no mips, clamp on...
     

    Attached Files:

    Last edited: Aug 1, 2014
    CoughE likes this.
  13. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    Updated:
    • fixed weird jumping of windows when resizing
    • fixed loosing mouse drag handles when cursor moves outside handle area
    • added a nice way to handle Bezier aliasing (and a free PNG attached)
    • dropped shadow stuff (easily added based on original code)
    • (fixed) issue: if mouseup occurs outside parent window boundaries, _handleActive remains true
    • (fixed) issue: seems like the parent window become modaless at some point.
    • (note, looks like the above two issues are related to not setting a hotcontrol on the handle area)
    • (minor) issue: when mouse is down outside of handle area in primary editor window and moved into a handle area (making it hot), there is a slight resize made. probably easily fixable, but that's all I can add for now.

    I really wish GUI.WindowFunction could be extended somehow so I could pass in oh... a generic.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System;
    6. using System.Reflection;
    7. using System.Text;
    8.  
    9. public class NodeEditorWindow  : EditorWindow
    10. {
    11.     static NodeEditorWindow  window;
    12.  
    13.     public Rect window1, window2, _handleArea;
    14.     private bool _nodeOption, _options, _handleActive, _action;
    15.     private Texture2D _resizeHandle, _aaLine;
    16.     private GUIContent _icon;
    17.     private float _winMinX, _winMinY;
    18.     private int _mainwindowID;
    19.  
    20.     [MenuItem("Window/Node Editor")]
    21.     static void Init()
    22.     {
    23.         window = (NodeEditorWindow )EditorWindow.GetWindow(typeof(NodeEditorWindow));
    24.         window.title = "Node Editor";
    25.         window.ShowNodes();
    26.     }
    27.  
    28.     private void ShowNodes()
    29.     {
    30.         _winMinX = 100f;
    31.         _winMinY = 100f;
    32.         window1 = new Rect(30, 30, _winMinX, _winMinY);
    33.         window2 = new Rect(210, 210, _winMinX, _winMinY);
    34.  
    35.         _resizeHandle = AssetDatabase.LoadAssetAtPath("Assets/NodeEditor/Icons/ResizeHandle.png", typeof(Texture2D)) as Texture2D;
    36.         _aaLine = AssetDatabase.LoadAssetAtPath("Assets/NodeEditor/Icons/AA1x5.png", typeof(Texture2D)) as Texture2D;
    37.         _icon = new GUIContent(_resizeHandle);
    38.         _mainwindowID = GUIUtility.GetControlID(FocusType.Native); //grab primary editor window controlID
    39.     }
    40.  
    41.     void OnGUI()
    42.     {
    43.         BeginWindows();
    44.         window1 = GUI.Window(1, window1, DrawNodeWindow, "Window 1");   // Updates the Rect's when these are dragged
    45.         window2 = GUI.Window(2, window2, DrawNodeWindow, "Window 2");
    46.         EndWindows();
    47.  
    48.         DrawNodeCurve(window1, window2);
    49.  
    50.         GUILayout.BeginHorizontal(EditorStyles.toolbar);
    51.         _options = GUILayout.Toggle(_options, "Toggle Me", EditorStyles.toolbarButton);
    52.         GUILayout.FlexibleSpace();
    53.         GUILayout.EndHorizontal();
    54.        
    55.         //if drag extends inner window bounds _handleActive remains true as event gets lost to parent window
    56.         if ((Event.current.rawType == EventType.MouseUp) && (GUIUtility.hotControl != _mainwindowID))
    57.         {
    58.             GUIUtility.hotControl = 0;
    59.         }
    60.     }
    61.  
    62.     private void DrawNodeWindow(int id)
    63.     {
    64.         if (GUIUtility.hotControl == 0)  //mouseup event outside parent window?
    65.         {
    66.             _handleActive = false; //make sure handle is deactivated
    67.         }
    68.        
    69.         float _cornerX = 0f;
    70.         float _cornerY = 0f;
    71.         switch (id) //case which window this is and nab size info
    72.         {
    73.             case 1:
    74.                 _cornerX = window1.width;
    75.                 _cornerY = window1.height;
    76.                 break;
    77.             case 2:
    78.                 _cornerX = window2.width;
    79.                 _cornerY = window2.height;
    80.                 break;
    81.         }
    82.  
    83.         //begin layout of contents
    84.         GUILayout.BeginArea(new Rect(1, 16, _cornerX - 3, _cornerY - 1));
    85.         GUILayout.BeginHorizontal(EditorStyles.toolbar);
    86.         _nodeOption = GUILayout.Toggle(_nodeOption, "Node Toggle", EditorStyles.toolbarButton);
    87.         GUILayout.FlexibleSpace();
    88.         GUILayout.EndHorizontal();
    89.         GUILayout.EndArea();
    90.  
    91.         GUILayout.BeginArea(new Rect(1, _cornerY - 16, _cornerX - 3, 14));
    92.         GUILayout.BeginHorizontal(EditorStyles.toolbarTextField, GUILayout.ExpandWidth(true));
    93.         GUILayout.FlexibleSpace();
    94.  
    95.         //grab corner area based on content reference
    96.         _handleArea = GUILayoutUtility.GetRect(_icon, GUIStyle.none);
    97.         GUI.DrawTexture(new Rect(_handleArea.xMin + 6, _handleArea.yMin - 3, 20, 20), _resizeHandle); //hacky placement
    98.         _action = (Event.current.type == EventType.MouseDown) || (Event.current.type == EventType.MouseDrag);
    99.         if (!_handleActive && _action)
    100.         {
    101.             if (_handleArea.Contains(Event.current.mousePosition, true))
    102.             {
    103.                 _handleActive = true; //active when cursor is in contact area
    104.                 GUIUtility.hotControl = GUIUtility.GetControlID(FocusType.Native); //set handle hot
    105.             }
    106.         }
    107.  
    108.         EditorGUIUtility.AddCursorRect(_handleArea, MouseCursor.ResizeUpLeft);
    109.         GUILayout.EndHorizontal();
    110.         GUILayout.EndArea();
    111.  
    112.         //resize window
    113.         if (_handleActive && (Event.current.type == EventType.MouseDrag))
    114.         {
    115.             ResizeNode(id, Event.current.delta.x, Event.current.delta.y);
    116.             Repaint();
    117.             Event.current.Use();
    118.         }
    119.  
    120.         //enable drag for node
    121.         if (!_handleActive)
    122.         {
    123.             GUI.DragWindow();
    124.         }
    125.     }
    126.  
    127.     private void ResizeNode(int id, float deltaX, float deltaY)
    128.     {
    129.         switch (id)
    130.         {
    131.             case 1:
    132.                 if ((window1.width + deltaX) > _winMinX) { window1.xMax += deltaX; }
    133.                 if ((window1.height + deltaY) > _winMinY) { window1.yMax += deltaY; }
    134.                 break;
    135.             case 2:
    136.                 if ((window2.width + deltaX) > _winMinX) { window2.xMax += deltaX; }
    137.                 if ((window2.height + deltaY) > _winMinY) { window2.yMax += deltaY; }
    138.                 break;
    139.         }
    140.     }
    141.  
    142.     void DrawNodeCurve(Rect start, Rect end)
    143.     {
    144.         Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0);
    145.         Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0);
    146.         Vector3 startTan = startPos + Vector3.right * 50;
    147.         Vector3 endTan = endPos + Vector3.left * 50;
    148.         Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, _aaLine, 1.5f);
    149.     }
    150. }
    ;)
     

    Attached Files:

    Last edited: Aug 2, 2014
    Ruhan-Bello and moure like this.
  14. CitrusDev

    CitrusDev

    Joined:
    Jul 3, 2012
    Posts:
    115
    thanks that is really cool !!

    is it possible to change the color of the bezier curve after the line was clicked ?
     
  15. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    I'm sure you can... I would probably just add support for that event state in OnGUI and modify DrawNodCurve to handle the color input you want based on that state.
     
  16. TehWardy

    TehWardy

    Joined:
    Mar 20, 2013
    Posts:
    38
    How do I use this as an editor for a property on a monobehavior?
    I can't figure out how to trigger this passing the property reference in.

    I'm building a node based noise module editor.
     
  17. ChanMOOOOR

    ChanMOOOOR

    Joined:
    May 7, 2014
    Posts:
    10
    HI, your script helped me a lot, however i'm facing a huge problem that i can not solve.
    Here is my idea , in short ,part of my idea:
    1.using a List to store the Window's infomations such as Rect and parent node,child nodes and so on .
    2.In OnGUI ,there is a " for " loop to draw each window by the List 's count and it's information.
    3.on user click add node , i always add new one to he List.
    4.after click delete button i remove current focus window from the List

    Now if i delete the node by the order i add them in. Everything works fine. However , I'm delete the node between the order I got Crash ( 1,2,3 i delete 2),which is an" Index out of bounds exception". anything wrong ?

    some code to express:
    Code (CSharp):
    1.  
    2. void OnGUI()
    3. {
    4.     DrawNodes();
    5. }
    6.  
    7. void DrawNodes()
    8. {
    9.     for(int i = 0;i < windowList.Count;i++)
    10.     {
    11.         ..........
    12.          windowList[i].windowArea = GUI.Window(windowList[i].windowIndex,windowList[i].windowArea,OnDrawWindow,windowList[i].windowTitle);
    13.     }
    14.  
    15. void OnDrawWindow(int id)
    16. {
    17.     //Draw some layouted content
    18.     .........
    19.     if(Event.current.type == EventType.KeyDown && Event.current.keycode == KeyCode.Delete)
    20.     {
    21.         windowList.RemoveAt(id);
    22.         Repaint();
    23.     }
    24. }
    25. }
    Any help ? if i fixed this problem, that's the time to post the code here!!!
     
  18. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    perhaps it has something to do with your callback for ondrawwindow.. as you're asking it to draw the windows, then within, you're trying to remove it causing the error?

    I would say moving the event handler to separate method to be called after or before drawnodes() might fix the issue.
     
  19. ChanMOOOOR

    ChanMOOOOR

    Joined:
    May 7, 2014
    Posts:
    10
    Thank you tswalk , however your suggestion does't work as well.
    Maybe i need to find some documents about the OnGUI lifetime circle detail . if Repaint will break current frame and run another OnGUI that will fix it . because i Debug.Log() current windowList.Count .the length is correct,but it still called the one whick out of bounds .
     
  20. XuPoH

    XuPoH

    Joined:
    Oct 4, 2014
    Posts:
    13
    Anybody needs a perfect version of node editor? I've added a few functions such as multiply root-child nodes, context menu with adding nodes, connecting root and child nodes. Btw I created it for my dialog system.
     
    Last edited: Feb 15, 2015
  21. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    @XuPoH: Are you offering updated code?

    If so, and you don't mind contributing, just go ahead and post it!

    I'm sure the community will appreciate it.
     
  22. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    indeed.. please do share!
     
  23. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    I take the delay and lack of further posts as a no. meh, that's fine, I've completely re-written it... I'm just thankful for the original contribution that took me on this journey.
     
  24. XuPoH

    XuPoH

    Joined:
    Oct 4, 2014
    Posts:
    13
    Check out my profile. There you can find my post. It's title starts with "[Bad Code, sry]"
     
    tswalk likes this.
  25. XuPoH

    XuPoH

    Joined:
    Oct 4, 2014
    Posts:
    13
    But it seems that it does not works..
     
  26. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    I'm not sure I understand what you meant?

    I checked your project files... the nodes seem to be basically working.
     
  27. XuPoH

    XuPoH

    Joined:
    Oct 4, 2014
    Posts:
    13
    I've made a bad structure, so that on compile you'll got errors.
     
  28. AthosK

    AthosK

    Joined:
    Jan 20, 2013
    Posts:
    428
    Last edited: Jul 10, 2015
  29. delphinius81

    delphinius81

    Joined:
    Mar 6, 2012
    Posts:
    57
    You have no idea how happy I am to see these posts! Excellent resources for building a more advanced editor tool. Thank you everyone!
     
    AthosK likes this.
  30. misterPantoni

    misterPantoni

    Joined:
    Feb 7, 2013
    Posts:
    44
    This is excellent - thank you a lot, now i understand a lot of the whole editor-window thing way better. Thanks!
     
    AthosK likes this.
  31. Stiffx

    Stiffx

    Joined:
    Oct 27, 2014
    Posts:
    455
    Very nice :) thank you for that... Only one question: What about scrolling when the graph runs too big? This is my life time question at unity editor scripting. I've always had problem doing that.

    Thanks anyway
     
  32. AthosK

    AthosK

    Joined:
    Jan 20, 2013
    Posts:
    428
    Check the second script posted by unimechanic, you can do it using GUI.BeginGroup()
    If you don't want to use the buttons as in that example you can check the events for the middle mouse and start adding any changes on the mouse position to the PanY/PanX values. Haven't done it but it should be fairly simply to do
     
    Stiffx likes this.
  33. Stiffx

    Stiffx

    Joined:
    Oct 27, 2014
    Posts:
    455
    Thanks I'll do
     
    AthosK likes this.
  34. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    Based on my experience so far, as long as you are only concerned with "content" that extends into positive space, you can easily use the GUI.BeginScrollView to deal with it; however, in the case of using nodes that can be moved into negative content space... it breaks, as none of the built in GUI nor GUILayout utilities that deal with horizontal, vertical, or scrollviews are capable of handling negative scrolling and offsets.

    I would (and have for my projects) use a utility class to handle this by calculating a "virtual" frame containing the nodes, taking the offset values, and combining them into a simple "GUI.HorizontalScollBar" (or vertical) method, and deal with negative space scrolling and offsets myself. It works quite well.
     
  35. AthosK

    AthosK

    Joined:
    Jan 20, 2013
    Posts:
    428
    I've added some more examples for this like enabling/disabling a gameobject , finding the distance between two GOs and a timer node.

    Edit: see my first post for links

    and of course a video if you need more explanation on the scripts
     
    Last edited: Jul 10, 2015
  36. Zyxil

    Zyxil

    Joined:
    Nov 23, 2009
    Posts:
    111
    To make a scrollable area just do this:

    (typing from memory, so treat as pseudocode)

    Code (CSharp):
    1. EditorGUILayout.BeginScrollView();
    2.  
    3. GUILayout.LabelField(Height(4000),Width(4000));
    4.  
    5. //draw your nodes here
    6.  
    7. EditorGUILayout.EndScrollView();
     
  37. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    402
    Here's an upgraded version of DrawCurve which allows you to specify which corner the line attaches to:


    Code (csharp):
    1.  
    2.     void DrawNodeCurve(Rect start, Rect end)
    3.     {
    4.         DrawNodeCurve(start, end, new Vector2(1.0f, 0.5f), new Vector2(1.0f, 0.5f));
    5.     }
    6.  
    7.     void DrawNodeCurve(Rect start, Rect end, Vector2 vStartPercentage, Vector2 vEndPercentage )
    8.     {
    9.         Vector3 startPos = new Vector3(start.x + start.width * vStartPercentage.x, start.y + start.height * vStartPercentage.y, 0);
    10.         Vector3 endPos = new Vector3(end.x + end.width * vEndPercentage.x, end.y + end.height * vEndPercentage.y, 0);
    11.         Vector3 startTan = startPos + Vector3.right * (-50 + 100 * vStartPercentage.x) + Vector3.up * (-50 + 100 * vStartPercentage.y);
    12.         Vector3 endTan = endPos + Vector3.right * (-50 + 100 * vEndPercentage.x) + Vector3.up * (-50 + 100 * vEndPercentage.y);
    13.         Color shadowCol = new Color(0, 0, 0, 0.06f);
    14.         for (int i = 0; i < 3; i++) // Draw a shadow
    15.             Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5);
    16.         Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 2);
    17.     }
    18.  
    animationblender_drawexample.png
     
    Ruhan-Bello, howong, Yuanfeng and 2 others like this.
  38. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    402
    And here's a very simple Arrowhead drawing formula:

    Code (csharp):
    1.  
    2.  
    3.     void DrawArrowhead(Rect start, Rect end, Vector2 vStartPercentage, Vector2 vEndPercentage, float fHandleDistance, float fLength, float fWidth)
    4.     {
    5.         float fHandleDistanceDouble = fHandleDistance * 2;
    6.  
    7.         Vector3 startPos = new Vector3(start.x + start.width * vStartPercentage.x, start.y + start.height * vStartPercentage.y, 0);
    8.         Vector3 startTan = startPos + Vector3.right * (-fHandleDistance + fHandleDistanceDouble * vStartPercentage.x) + Vector3.up * (-fHandleDistance + fHandleDistanceDouble * vStartPercentage.y);
    9.        
    10.         Vector3 endPos = new Vector3(end.x + end.width * vEndPercentage.x, end.y + end.height * vEndPercentage.y, 0);
    11.         Vector3 endTan = endPos + Vector3.right * (-fHandleDistance + fHandleDistanceDouble * vEndPercentage.x) + Vector3.up * (-fHandleDistance + fHandleDistanceDouble * vEndPercentage.y);
    12.  
    13.         float dy = endTan.y - endPos.y;
    14.         float dx = endTan.x - endPos.x;
    15.  
    16.         Vector3 vDelta = endTan - endPos;
    17.         Vector3 vNormal = new Vector3 ( -dy, dx, 0f ).normalized;
    18.        
    19.         Vector3 vArrowHeadEnd1 = endPos + vDelta.normalized * fLength + vNormal.normalized * fWidth;
    20.         Vector3 vArrowHeadEnd2 = endPos + vDelta.normalized * fLength + vNormal.normalized * -fWidth;
    21.  
    22.         Vector3 vHalfwayPoint = endPos  + vDelta.normalized * fLength * 0.5f;
    23.  
    24.         Color shadowCol = new Color(0, 0, 0, 0.06f);
    25.        
    26.         for (int i = 0; i < 3; i++) // Draw a shadow
    27.             Handles.DrawBezier(endPos, vArrowHeadEnd1, endPos, vHalfwayPoint, shadowCol, null, (i + 1) * 5);
    28.         Handles.DrawBezier(endPos, vArrowHeadEnd1, endPos, vHalfwayPoint, Color.black, null, 2);
    29.  
    30.         for (int i = 0; i < 3; i++) // Draw a shadow
    31.             Handles.DrawBezier(endPos, vArrowHeadEnd2, endPos, vHalfwayPoint, shadowCol, null, (i + 1) * 5);
    32.         Handles.DrawBezier(endPos, vArrowHeadEnd2, endPos, vHalfwayPoint, Color.black, null, 2);
    33.     }
    34.  
    Usage:

    Code (csharp):
    1.  
    2. DrawArrowhead(rRawPrefabBox, rChildObjectBox, new Vector2(0.25f, 1.0f), new Vector2(0.0f, 0.5f), 50f, 30f, 10f);
    3.  
    animationblender_arrowdrawexample.png
     
    howong likes this.
  39. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey everyone,

    Over two years of collaboration and constant development, the opensource Node Editor Framework has emerged out of this thread - thanks to lot's of great feedback by the community:)
    I started developing it end of May 2015, but I'm by far not the only contributor anymore...
    Take a look:




    Major features:
    • Convenient editor with zooming and panning capabilities
    • Advanced modular traversal system used to calculate, update or traverse the canvas in any other way
    • Custom drag'n'drop node connection system
    • Dynamic extension of Controls, Nodes, Connections, NodeCanvases and their behaviour without touching framework code
    • Extensive control over Node/Knob/Connection appearance and generally modifiable GUI skin
    • Save/Load node canvas to asset or scene with auto-save functionality
    • Growing runtime support, both for traversing and even editing the canvas in-game
    • Documentation and active support:)
    • Many great examples found in the repo branches

    More information can be found on the repository:)

    Best regards,
    Seneral
     
    Last edited: Mar 5, 2019
  40. forestrf

    forestrf

    Joined:
    Aug 28, 2010
    Posts:
    225
    It shows this error:

    Code (CSharp):
    1. Assets/Node_Editor/Node_Editor.cs(82,17): error CS0103: The name `CommonUtility' does not exist in the current context
    Thank you!
     
  41. DaDarkDragon

    DaDarkDragon

    Joined:
    Jun 6, 2013
    Posts:
    115
    i really want to do something with these but i never know what.
     
  42. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Sorry about that, it's a utility I usually use in every of my editor extensions, this time I didn't use it though and forgot to remove this line. I updated the unitypackage above with a new version, which also solves some other wrong path references I missed to change:D

    Additionally, I completely reworked the Save/Load System, it's now way more efficient and also saves reliable. Still uses ScriptableObject.
    So you should be able to open the canvas save file I included (Assets/Node_Editor/Saves/Calculation Canvas.asset) correctly. If not, please report to me. And for some reason the side window got brighter even if I did not change anything:confused:
     
    Last edited: May 30, 2015
    forestrf likes this.
  43. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    You can do a hell lot with nodes indeed. In the last time a lot of State Machines / visual scripting tools using nodes became popular, and thats only a bit of what you can make.
    You could implement an algorithm with nodes; For example I'm working on 2d water simulation and erosion algorithm on the GPU. I could make it modular with the Node editor, so the Dev can create templates for disabling/enabling components and use them at runtime.
    You just need to have an idea and in most cases they can benefit from nodes, thats the coolest part! :D
     
  44. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    A small update:
    The functions are now correctly documented in the code and I changed some functions to be instance methods instead of statics which should be a lot easier to understand now. Though be aware of that when alot of errors are spitted out next time in the old scripts:)

    Also fixed a serious bug in the Save/Load system.

    I attached the update.

    Seneral
     

    Attached Files:

    Last edited: Jun 1, 2015
  45. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Just a quick screen of what you can do:

    Texture Composer.jpg

    The only major thing I changed to the framework so far is a color and custom textures associated with each type:
    Magenta is a Texture2D and yellow (or what should be yellow) is a channel (float[,]).
    The screen shows how to mix channels of two or more textures:)

    FYI all in the above screen was deleted by unity itself due to an asset importer crash. And two other editor extensions of mine as well. That's our stable Unity how I like it.
     
    Last edited: Jun 3, 2015
  46. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109

    looks great m8!
     
    Seneral likes this.
  47. kholyphoenix1

    kholyphoenix1

    Joined:
    Apr 17, 2015
    Posts:
    57
    People good day. I use Unity 5.
    I wonder if this script awarded by "Seneral" works with Unity 5.
    If so just drag into the engine and ready? Then how do I find the "Node Editor"? Thank you !!
     
  48. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    If you imported it you can access it through "Window/Node Editor". However it's a framework to make your own editor extension, so you will have to understand the code anyway to work your own tool out of it;) I recommend you to study it, it shouldn't be too hard. And yes it works fine with Unity 5.
     
  49. kholyphoenix1

    kholyphoenix1

    Joined:
    Apr 17, 2015
    Posts:
    57
    Seneral

    Well, I got your package and threw into the unity he asked if I would like to import said yes after it went on the menu windows look node editor and found. I think it only works in Unity 4.

    Thanks!!
     
  50. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    So you found it you say? If not, you probably have some other errors hanging around in your project. You have to fix them before the Menu Item will pop up. But it definitely works in Unity 5 because I made it with Unity 5;)

    Btw, If it would not work in U5 it would ask you to convert the files and/or spit out alot of errors!