Search Unity

Simple node editor

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

  1. DanieLoche

    DanieLoche

    Joined:
    Mar 17, 2017
    Posts:
    3
    Thanks a lot for your answer !

    Yep it is mostly the structure part of the coding that I want to adapt. I suppose it won't be hard to create nodes with cubes and so on.
    Sadly I don't have Unity installed there for now, but ASAP I'll see what I can do with this framework. :)

    About the 3D aspect, I was thinking about a force-based model, like this one (link). Like for example if you insert a node is goes to a default position and you can drag-and-move it around. And if you link it to other nodes, then its position will be set up naturally by the forces principle.

    The hardest part for me seems to manage in creating a game that will allow the player to create nodes and connect them creating lines between them. That's why I hope that such framework could help me to know how to do that. :)
     
  2. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Sure, you can use different representations for the nodes, think your best bet is to recreate the canvas hierarchy as gameobjects as you said so they are still scriptable and can be interacted with:)
    Wish you good luck - be sure to post your results here when you have some, would love to see how something like this turns out:)
     
  3. JKLING

    JKLING

    Joined:
    Nov 17, 2016
    Posts:
    1
    Very Good Job! I Love You baby:D...very much!!:)
     
  4. TheD0ctor

    TheD0ctor

    Joined:
    Sep 29, 2016
    Posts:
    13
    Hi @Seneral, this is an amazing project! I'm currently using a fork of it as the basis for a Node Editor for Crystal AI (https://theldoctor.github.io/CrystalAI/). By way of thanks for sharing with the community, I'd be delighted to give you a free license of the commerical version of Crystal AI, if that's of any interest to you of course. Also, I'm more than happy to contribute back any features you find in the modified editor that you like, once the editor feature is released on the asset store that is, as in its current state it is somewhat "unclean".
     

    Attached Files:

    Seneral likes this.
  5. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Thanks @JKLING lol

    Looks great already! Well background is a bit weird lol
    Would appreciate any contribution, will definitely take a look at it when it's done :)
     
    TheD0ctor likes this.
  6. TheD0ctor

    TheD0ctor

    Joined:
    Sep 29, 2016
    Posts:
    13

    Well, the background has changed since yesterday ;)
     
    Seneral likes this.
  7. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Hi

    I've been looking for a node editor for a long time, and have tried many different solutions. But THIS it the sh*t! :D
    It looks to be the best one to our needs. :)

    EDIT: We're going to use it as a standalone app. ;)

    However, I do find it a bit wierd (after having read the entire thread) that:
    • There's no implementation of the delete key?
    • Nobody seems to mind that the context menu dissappears when moving the mouse away?
    The first issue is an easy fix that even I can do. :)

    Code (CSharp):
    1.  
    2.         [ContextEntryAttribute (ContextType.Node, "Delete Node")]
    3.         [HotkeyAttribute(KeyCode.Delete, EventType.KeyDown)]
    4.         private static void DeleteNode (NodeEditorInputInfo inputInfo)
    5.         {
    6.             inputInfo.SetAsCurrentEnvironment ();
    7.             NodeEditorState state = inputInfo.editorState;      
    8.             if (state.focusedNode != null)
    9.             {
    10.                 state.focusedNode.Delete ();
    11.                 inputInfo.inputEvent.Use ();
    12.             }
    13.             else if (state.selectedNode != null)
    14.             {
    15.                 state.selectedNode.Delete();
    16.                 inputInfo.inputEvent.Use();
    17.             }
    18.         }
    19.  
    The other one, however, I have no clue as to how to fix it.... :-/
     
    Last edited: Apr 6, 2017
  8. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey Jamsa,
    thanks alot for your kind words:)

    Yes indeed, no delete key yet - if you want, can you make a commit with that addition? If not I can commit it:)
    Looks like a good implementation, but this will also delete the node when you're simply hovering over it, even if you have a different node selected. Maybe exchange the if clauses, should cause no problem afaik in that the right-click on hovered nodes still works.

    Second one is intentional, isn't that expected with context clicks? I found it more intuitive but it can be changed relatively easy. The Node Editor Framework uses a custom generic menu for context menus (because of scaling) so you can change this behaviour in Utilities/GUI/OverlayGUI.cs (not tested):
    Line 184, in the Method Draw, add
    Code (csharp):
    1. bool clicked = Event.current.type == EventType.MouseDown;
    and change
    Code (csharp):
    1. if (!inRect || close)
    2.     OverlayGUI.ClosePopup ();
    later on to:
    Code (csharp):
    1. if (clicked)
    2.     OverlayGUI.ClosePopup ();
    which should close the popup only when an item was clicked or it was clicked outside of the menu, if that's what you want:)

    Hope this helps,
    Seneral
     
  9. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    No no, thank you. :)

    I'm not really into all that commit-stuff yet, so you want to, then please do it. :)

    Yes, I'm aware that it gives two more ways of deleting an object. :D Change it, if you want. :)


    Wow...I didn't think anyone would like it as it is!! :O :D

    Where I'm from (windows world) context menys alway stay open until you click or type. :)

    Cool, I'll try it and see if it works as you say. ;)
     
  10. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Awesome, your fix works perfectly! :)
     
  11. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Cool!
    Funnily enough, I'm also on windows but still prefer the current way:D
    If everyone else prefers the context menus to stay when you move the mouse away, I'd happily change that.
    Also will create a commit for that when I have time:)
     
  12. esteban_arrua

    esteban_arrua

    Joined:
    Jan 25, 2017
    Posts:
    1
    Hi, I am trying to do mi own Node editor, and I need to do zoom, and move the window. Mi problem is the following, when I do zoom, the nodes that were incomplete because they didn't enter in the window, continue incomplete. Like the images show.
    Captura de pantalla 2017-04-10 a la(s) 13.15.21.png Captura de pantalla 2017-04-10 a la(s) 13.15.58.png Any idea how can I resolve this?
    Thanks.
    Esteban.
     
  13. Seneral

    Seneral

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

    yes this is a serious flaw in the unity GUI system, it unfortunately doesn't handle scaling very well because it does not scale the clipping rects and thus clips all GUI elements outside of the original clipping rect.
    There is one good solution on a few blog posts, just search them.
    But they all have a flaw themself, the view that you want to scale has to be at (0,0) of your editor window/game view and it also scales only to (0,0).
    So I made a generic solution hacking into the GUI grouping system that solves both the clipping rect and the forced scale origin, but requires you to offset all GUI elements by a returned value (in our case only the nodes).
    You can get the script in my github gist and also on the node editor repository:)


    Hope this helps you,
    Seneral
     
  14. Xabiru66

    Xabiru66

    Joined:
    Apr 7, 2015
    Posts:
    1
    Hey Seneral, first thing great fking work. I don't know really much about programming, and your editor saved my day. It's impressive.
    Second thing, I'm here to report a couple things you probably have noticed, just wondering if its wip or there is no way around:
    -If a node isnt selected before clicking on a field(GameObject field, EnumPopups, etc) changes arent saved(drag and drop works well, even if the node isn't selected).
    -Popup windows on EnumPopups show on diferent positions depending on zoom(img).
    Once again, great work and thank you.
    Edit: just came across something, while hovering using UnityEngine; it shows the error CS0103, saying "The name 'UnityEngine' does not exist in the current context". As far as I know it has something to do with the framework, but I have no idea on hoow to fix it.
     

    Attached Files:

    Last edited: Apr 18, 2017
  15. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Lol, then you must know that staying open is The Windows Way. ;) Anyway do as you want, and I'll do the same. :D

    Next I need to do saving in our own format. I guess it's supposed to be done in the save manager? :)

    Right around here:
    Code (CSharp):
    1.  
    2.         #else
    3.             // TODO: Node Editor: Need to implement ingame-saving (Resources, AsssetBundles, ... won't work)
    4.         #endif
     
  16. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey Xabiru, sorry for the late reply - just came back from a 10-day trip this morning...
    Glad you're happy with the framework so far:)
    Yes, first one is a known issue for quite some time, it has to do with the ordering if the nodes changing when clicking on them, but the real problem lies because some controls, like the popups and picker fields, don't let ANY event through until they are closed, which causes the nodes to shuffle and reorder when these changes should be applied, exchanging the control IDs and thus messing up the node the popup/picker values are assigned to.
    Currently, I've no clue how to fix it - there is a way, but that would require the nodes to stay in order and thus if you select a node it might still be in the background.

    Second one is equally well known and equally annoying - and that can't be fixed at all. The GUI scaling in Unity seems to have been implemented in a hurry:( One big problem, zoom position and clipping issues that are caused by it, I have fixed already through a hack, but this one simply disregards the GUI scale when positioning the popups... only way to overcome this is to replace all popup controls, which I actually already started (you see it as the context click) and could easily be expanded on other popup controls:)

    Don't know what you mean by the last error, it's probably just a glitch in MonoDevelop... restarting Unity should fix that:)

    Best regards,
    Seneral
     
  17. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ah ok - don't hesitate to push a change, probably more intuitive for most, and I don't really care if it closes or not when moving the mouse away... for me, it's just a click less incase I made an error;)

    Cool - many actually need a runtime save format like JSON or XML, would really appreciate any approach to it:)
    Yes, that is the place you could put your export logic. Though, I'd recommend keeping it as independant from the framework as possible. If you're using an automated JSON serializer it might be as simple as calling one function and preparing the data structures...
    In the future I could think of another dynamic extension system that automatically fetches all export/import types that are available (JSON, XML, plain text, idk, anything really), but for now that will suffice:)
    If you happen to implement a custom export type that can be used at runtime, I'd really appreciate a contribution/commit/post here;)

    Best regards,
    Seneral
     
  18. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Heh, as I stated before, I'm not into all that commit-stuff yet, ;) Soo, please do it in your own good time. :)

    Well, actually we need it our in format, so a generic format will not be made by me. Sorry. ;)

    But the precompiling of data might be useable to others. :)

    Could you point me in to right direction? I'm having trouble finding the right way to access data when saving during runtime.
     
  19. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Well if you have a custom data structure - like another scriptable data system or strictly regulated data format - and you have no automatic parsers like with JSON or XML or Binary, you'd have to manually iterate through each data point (canvas, nodes, knobs, etc...) and copy the required data into your new data format. For node-specific data, you would have to manually return the data for each node by overriding a method I would assume. It's nasty but faster than writing an automated parser with reflection from scratch I would assume (never wrote one).
    Sorry cannot help much more than this. I would prefer the automated JSON serialization, as it's the most generic way, but I see it's not an option for you when you have strict requirements...

    Best regards,
    Seneral
     
  20. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Actually it's a database, so getting data in and out of that will be easy for me. :)

    However getting data out from the editor is something that's a bit hard for me, as I'm new to unity.

    Could you give me a quick example as to how to iterate the objects? :)
     
  21. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ah ok, wasn't sure what you meant by guide:)
    Fortunately there are already a few good examples in the framework, the best I guess would be CreateWorkingCopy at the bottom of NodeEditorSaveManager.
    Another one with a bit different approach is the default calculation routine, which can be found in the Default folder - this uses the structure to linearly traverse the canvas instead of just iterating over all nodes.
    Btw, do you need a one-way-export that only includes node content or also the node data so you can recreate the canvas from that database?
    Hope these help you:)
    Best regards,
    Seneral
     
  22. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    ...and those ways also work during runtime? :)

    Well, I would need to be able to save and load the graph. So no one-way-export shortcut. ;)

    I guessing our users would find it a bit annoying if they couldn't change their graphs the next day! :D
     
  23. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Haha true:D
    Yes sure, they work at runtime - the whole data structure of the canvas is completely accessible and the same as in the editor at runtime :)
     
  24. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Awesome, thanks! :)

    I'll get cracking! ;)
     
    Seneral likes this.
  25. M_Dizzle

    M_Dizzle

    Joined:
    Dec 10, 2013
    Posts:
    20
    Hi @Seneral! I've not been able to look at this thread in ages, but I've also been looking into runtime saving for the last few days, so I thought I'd share what I've learnt so far.

    I tried using XmlSerializer, but perhaps because of the way I'm trying to save my NodeInputs and Outputs it throws a Circular Reference detected error which I can't figure out.

    So instead I've started writing my own Xml using reflection which so far seems to be going OK - although I haven't actually gotten as far as trying to write an importer yet!

    My intended file format will be something like this;

    Code (CSharp):
    1. <canvas type="CanvasType">
    2.    <nodes>
    3.       <node id="NodeEditorFramework.DisplayNode:{index}">
    4.          <value>10</value>
    5.          ... public fields or properties (haven't decided yet)
    6.         <knobs>
    7.            <knob type="Input" id="NodeEditorFramework.DisplayNode:{index}|{knobIndex}">[Name]</knob>
    8.          </knobs>
    9.      </node>
    10.      ...
    11.   </nodes>
    12.   <connections>
    13.      <conn>
    14.         <from>NodeEditorFramework.CalcNode:0|2</from>
    15.         <to>NodeEditorFramework.DisplayNode:1|0</to>
    16.      <conn>
    17.      ....
    18.    </connections>
    19. </canvas>
    The idea is to save each of the nodes and then have a separate list of connections that use the IDs created in the file to make the connections programmatically on load.

    It's a bit fiddly, but since using XmlSerializer seems like a non-starter, it seems like the best way to go.

    What do you think?
     
  26. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    That seems like a promising way to do this:)
    Making it manually, atleast for the framework structure itself, is less fiddly than using an automated solution and more safe. No need to write an automatic reflection based serializer though:)
    Just think for the connections, it's best to keep them back in the nodes, like the real framework structure - just needs a list of connections for each node (for both Input and Output knob to support future plans) which consists of the IDs you set up:) But instead of parsing the ID as a string to figure out the opposite site, I think it should keep all objects (Knobs, Nodes, etc - just every Framework scriptable object that could be referenced) with their IDs in a Dictionary, key and object being respectively switched for Parsing/Serializing the file.
    Then, for the Nodes, in order to serialize custom data, it would suffice to use an automated parser like the XML parser you used previously, as long as it supports custom attributes to mark the fixed node properties (like knobs) as non-serialized.
    Hope you understood what I mean:) So in the end, the framework structure is manually serialized using simple IDs for scriptable objects and an automated Serializer for the Node Contents.

    OR, another aproach, why not use a Unity-Specific serializer that automatically detects ScriptableObjects and assigns IDs to them? If there is any, that is.
     
  27. M_Dizzle

    M_Dizzle

    Joined:
    Dec 10, 2013
    Posts:
    20
    So I've had a look into what you've suggested, and like you said, so long as I add [XmlIgnore] to any references to NodeKnobs it compiles XML fine. It did mean that I had to edit the Node.cs file in the framework to add the attribute to the NodeKnobs, Inputs and Outputs lists though which I was hoping to avoid.

    What may complicate it slightly for me is that I've abstracted the nodes' internal interfaces and logic into reusable NodeControls which have their own NodeInputs built into them (like you might want a Vector3 control with x,y,z inputs on multiple different Nodes) so I'm hoping that whenever I create a new node with this method that the inputs always get added to the NodeKnobs array in the same order, otherwise I'll have to try to manually export the connections inside each control to keep the structure intact.

    So what I'm gonna try to do is save data using XMLSerializer and then manually insert the NodeKnobs array into the resulting XML
     
  28. M_Dizzle

    M_Dizzle

    Joined:
    Dec 10, 2013
    Posts:
    20
    Hi @Seneral - I've only gone and done it! Here's my code for anyone that's wanting to save at runtime. It needs some clean up and better formatting and is pretty untested and is just copied straight out my project - but it should be enough for others to build on.

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Reflection;
    7. using System.IO;
    8. using System.Xml;
    9. using System.Xml.Serialization;
    10. using NodeEditorFramework;
    11. using UnityEngine;
    12.  
    13. namespace NodeReactor
    14. {
    15.     public class NodeReactor
    16.     {
    17.         private static Dictionary<Node, string> NodeList;
    18.         private static Dictionary<NodeKnob, string> KnobList;
    19.  
    20.         public static void SaveXML(string path, ref NodeCanvas canvas)
    21.         {
    22.             NodeList = new Dictionary<Node, string>();
    23.             KnobList = new Dictionary<NodeKnob, string>();
    24.  
    25.             XmlDocument doc = new XmlDocument();
    26.             XmlElement root = doc.CreateElement("canvas");
    27.             doc.AppendChild(root);
    28.             XmlElement nodes0 = doc.CreateElement("nodes");
    29.  
    30.             //create IDs for nodes and knobs
    31.             int index0 = 0;
    32.             for (int t = 0; t < canvas.nodes.Count; t++)
    33.             {
    34.                 Node n = canvas.nodes[t];
    35.                 NodeList.Add(n, n.GetID + ":" + t);
    36.                 int knobIndex = 0;
    37.                 for (int kN = 0; kN < n.nodeKnobs.Count; kN++)
    38.                 {
    39.                     NodeKnob k = n.nodeKnobs[kN];
    40.  
    41.                     KnobList.Add(k, NodeList[n] + "|" + k.name + ":" + kN);
    42.                     knobIndex++;
    43.                 }
    44.                 index0++;
    45.             }
    46.  
    47.             //begin serialization of nodes
    48.             foreach (Node n in canvas.nodes)
    49.             {
    50.                 XmlSerializer s = new XmlSerializer(n.GetType());
    51.  
    52.                 StringWriter sw0 = new StringWriter();
    53.                 XmlWriter xW = XmlWriter.Create(sw0);
    54.  
    55.                 s.Serialize(xW, n);
    56.  
    57.                 XmlDocument nDoc = new XmlDocument();
    58.                 nDoc.LoadXml(sw0.ToString());
    59.                 XmlNode nNode = doc.DocumentElement.OwnerDocument.ImportNode(nDoc.DocumentElement, true);
    60.                 XmlAttribute id = doc.CreateAttribute("id");
    61.                 id.Value = NodeList[n];
    62.                 nNode.Attributes.Append(id);
    63.                 XmlAttribute type = doc.CreateAttribute("type");
    64.                 type.Value = n.GetType().ToString();
    65.                 nNode.Attributes.Append(type);
    66.  
    67.                 XmlElement connections = doc.CreateElement("connections");
    68.                 //find all inputs that have connections
    69.                 foreach (NodeInput input in n.Inputs.Where(x => x.connection != null))
    70.                 {
    71.  
    72.                     XmlElement activeInput = doc.CreateElement("input");
    73.                     XmlAttribute kId = doc.CreateAttribute("id");
    74.                     kId.Value = KnobList[input];
    75.                     activeInput.Attributes.Append(kId);
    76.                     activeInput.InnerText = KnobList[input.connection];
    77.                     connections.AppendChild(activeInput);
    78.                 }
    79.                 nNode.AppendChild(connections);
    80.  
    81.                 nodes0.AppendChild(nNode);
    82.                 index0++;
    83.  
    84.             }
    85.  
    86.             root.AppendChild(nodes0);
    87.  
    88.             StringWriter sw1 = new StringWriter();
    89.             XmlTextWriter xmlWr0 = new XmlTextWriter(sw1);
    90.             xmlWr0.Formatting = Formatting.Indented;
    91.             doc.WriteTo(xmlWr0);
    92.             Debug.Log(path);
    93.             doc.Save(path);
    94.             Debug.Log(sw1.ToString());
    95.  
    96.             sw1.Close();
    97.             xmlWr0.Close();
    98.  
    99.         }
    100.  
    101.         public static void LoadXml(string path)
    102.         {
    103.             NodeList = new Dictionary<Node, string>();
    104.             KnobList = new Dictionary<NodeKnob, string>();
    105.             XmlDocument doc = new XmlDocument();
    106.             doc.Load(path);
    107.  
    108.             //iterate through nodes
    109.             XmlNode nodelist = doc.GetElementsByTagName("nodes")[0];
    110.  
    111.             for (int i = 0; i < nodelist.ChildNodes.Count; i++)
    112.             {
    113.                 XmlNode n = nodelist.ChildNodes[i];
    114.                 XmlAttribute id = n.Attributes["id"];
    115.                 string nId = n.Attributes["id"].Value;
    116.                 string x = n.SelectSingleNode("rect/x").InnerText;
    117.                 string y = n.SelectSingleNode("rect/y").InnerText;
    118.                 Vector2 pos = new Vector2(float.Parse(x), float.Parse(y));
    119.                 Node newNode = Node.Create(nId.Split(':')[0], pos);
    120.  
    121.                 NodeList.Add(newNode, nId);
    122.                 int index = 0;
    123.                 foreach(NodeKnob k in newNode.nodeKnobs)
    124.                 {
    125.                     KnobList.Add(k, nId + "|" + k.name + ":" + index);
    126.                     index++;
    127.                 }
    128.  
    129.                 ProcessXmlNode(n, newNode);
    130.  
    131.             }
    132.  
    133.             //all nodes added, now go through list of nodes again and look for connections
    134.             for(int i = 0; i < nodelist.ChildNodes.Count; i++)
    135.             {
    136.                 XmlNode conns = nodelist.ChildNodes[i].SelectSingleNode("connections");
    137.                 if (conns.ChildNodes.Count > 0)
    138.                 {
    139.                     foreach(XmlNode input in conns.ChildNodes)
    140.                     {
    141.                         NodeInput inputKnob = KnobList.First(x => x.Value == input.Attributes["id"].Value).Key as NodeInput;
    142.                         NodeOutput outputKnob = KnobList.First(x => x.Value == input.InnerText).Key as NodeOutput;
    143.                         if (inputKnob.CanApplyConnection(outputKnob))
    144.                         {
    145.                             inputKnob.ApplyConnection(outputKnob);
    146.                         }
    147.                     }
    148.                 }
    149.             }
    150.  
    151.  
    152.  
    153.         }
    154.  
    155.         static void ProcessXmlNode(XmlNode n, object obj)
    156.         {
    157.             Type objType = obj.GetType();
    158.             //prepare array of public members
    159.             //bool - true for field, false for property
    160.             Dictionary<string, bool> objFields = new Dictionary<string, bool>();
    161.             foreach (FieldInfo f in objType.GetFields(BindingFlags.Public | BindingFlags.Instance))
    162.             {
    163.                 objFields.Add(f.Name, true);
    164.             }
    165.             foreach (PropertyInfo p in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
    166.             {
    167.                 objFields.Add(p.Name, false);
    168.             }
    169.  
    170.  
    171.             for (int nE = 0; nE < n.ChildNodes.Count; nE++)
    172.             {
    173.                 XmlNode cN = n.ChildNodes[nE];
    174.                 if (objFields.ContainsKey(cN.Name))
    175.                 {
    176.                     if (cN.ChildNodes.Count > 1)
    177.                     {
    178.                         //if it has children, then it must be a class, so grab the object and chuck it through this function again and pray
    179.                         System.Object cObj;
    180.                         if (objFields[cN.Name])
    181.                         {
    182.                             cObj = objType.GetField(cN.Name).GetValue(obj);
    183.  
    184.                         }
    185.                         else
    186.                         {
    187.                             cObj = objType.GetProperty(cN.Name).GetValue(obj, null);
    188.  
    189.                         }
    190.                         ProcessXmlNode(cN, cObj);
    191.  
    192.                     }
    193.                     else
    194.                     {
    195.  
    196.                         if (objFields[cN.Name])
    197.                         {
    198.                             FieldInfo f = objType.GetField(cN.Name);
    199.                             try
    200.                             {
    201.                                 f.SetValue(obj, Convert.ChangeType(cN.InnerText, f.FieldType));
    202.                             }
    203.                             catch (InvalidCastException e)
    204.                             {
    205.                                 Debug.Log(e.ToString());
    206.                             }
    207.                         }
    208.                         else
    209.                         {
    210.                             PropertyInfo p = objType.GetProperty(cN.Name);
    211.                             try
    212.                             {
    213.                                 p.SetValue(obj, Convert.ChangeType(cN.InnerText, p.PropertyType), null);
    214.                             }
    215.                             catch (InvalidCastException e)
    216.                             {
    217.                                 Debug.Log(e.ToString());
    218.                             }
    219.                         }
    220.  
    221.                     }
    222.                 }
    223.             }
    224.         }
    225.  
    226.  
    227.  
    228.     }
    229.  
    230.  
    231. }
    232.  
    It creates and reads files that look like this;

    Code (CSharp):
    1. <canvas>
    2.   <nodes>
    3.     <PassNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="passNode:0" type="NodeReactor.PassNode">
    4.       <name>Pass Filter</name>
    5.       <hideFlags>None</hideFlags>
    6.       <rect>
    7.         <x>-191</x>
    8.         <y>-100</y>
    9.         <position>
    10.           <x>-191</x>
    11.           <y>-100</y>
    12.         </position>
    13.         <center>
    14.           <x>-122.5</x>
    15.           <y>0</y>
    16.         </center>
    17.         <min>
    18.           <x>-191</x>
    19.           <y>-100</y>
    20.         </min>
    21.         <max>
    22.           <x>-54</x>
    23.           <y>100</y>
    24.         </max>
    25.         <width>137</width>
    26.         <height>200</height>
    27.         <size>
    28.           <x>137</x>
    29.           <y>200</y>
    30.         </size>
    31.         <xMin>-191</xMin>
    32.         <yMin>-100</yMin>
    33.         <xMax>-54</xMax>
    34.         <yMax>100</yMax>
    35.       </rect>
    36.       <backgroundColor>
    37.         <r>1</r>
    38.         <g>1</g>
    39.         <b>1</b>
    40.         <a>1</a>
    41.       </backgroundColor>
    42.       <constantUpdate>false</constantUpdate>
    43.       <pass>
    44.         <name>LOW PASS FILTER</name>
    45.         <type>LOW</type>
    46.         <filter />
    47.         <cutoffFrequency>1000</cutoffFrequency>
    48.         <quality>0.9</quality>
    49.       </pass>
    50.       <value>0</value>
    51.       <wibble>bibble</wibble>
    52.       <connections />
    53.     </PassNode>
    54.     <BlankNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="blankNode:1" type="NodeReactor.BlankNode">
    55.       <name>Blank node</name>
    56.       <hideFlags>None</hideFlags>
    57.       <rect>
    58.         <x>28</x>
    59.         <y>-60</y>
    60.         <position>
    61.           <x>28</x>
    62.           <y>-60</y>
    63.         </position>
    64.         <center>
    65.           <x>96.5</x>
    66.           <y>40</y>
    67.         </center>
    68.         <min>
    69.           <x>28</x>
    70.           <y>-60</y>
    71.         </min>
    72.         <max>
    73.           <x>165</x>
    74.           <y>140</y>
    75.         </max>
    76.         <width>137</width>
    77.         <height>200</height>
    78.         <size>
    79.           <x>137</x>
    80.           <y>200</y>
    81.         </size>
    82.         <xMin>28</xMin>
    83.         <yMin>-60</yMin>
    84.         <xMax>165</xMax>
    85.         <yMax>140</yMax>
    86.       </rect>
    87.       <backgroundColor>
    88.         <r>1</r>
    89.         <g>1</g>
    90.         <b>1</b>
    91.         <a>1</a>
    92.       </backgroundColor>
    93.       <constantUpdate>false</constantUpdate>
    94.       <value>0</value>
    95.       <wibble>wobble</wibble>
    96.       <connections>
    97.         <input id="blankNode:1|Input:0">passNode:0|Output:3</input>
    98.       </connections>
    99.     </BlankNode>
    100.   </nodes>
    101. </canvas>
     
  29. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Ok, after spending countless hour trying to get it to complile with sqlite dll's (and somehow it just fixed it self!?), I'm finally ready actually DO something! :D

    Now, as I'm still new to unity, I'm having a VERY hard time trying to find out how to access the active canvas object.

    Should be very simple, but not to me. :confused:
     
    Last edited: Apr 25, 2017
  30. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Aand, what do you know? I found it all by myself! :D Weee
     
  31. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Ok, now I've got a real issue. :D

    When making a graph, it possible to make it circular. But not using my own custom nodes.
    customeditor error not recursiv and textbox-label.png
    Why is that? The output file says: Cannot apply connection: Recursion detected!

    So I'm guessing that you have disabled this check somehow, but for all types of nodes?

    All my nodes are made from the ExampleNode example, as the docs say I should do. ;)


    Also: Adding a textbox on my node, makes the label-part a non-editable text field. How to fix??
     
  32. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey, sorry for the time off.

    @M_Dizzle Great work - seems like it can be improved a bit (see the rect, lot's of unnecessary information) but overal, great start! :)

    @Jamsa78 Cool you figured it out. To disable the recursion check on one node, you need to override the property 'AllowRecursion' in your custom nodes, but that assumes it handles recursion itself in case of failure! As the all around node does nothing of importance but passing the values along (it is a test), it does not need such protection...
     
  33. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Awesome, I KNEW you knew away around it. ;p As we NEED recursion, it's a feature, not a bug. :D

    No comment about the textbox thing?? ;)
     
  34. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey, sorry missed it.
    Seems like a bug in RTEditorGUI, assigns the style to both the label and the field...
    Check that script and replace the style in every call to
    Code (csharp):
    1. PrefixLabel (totalPos, 0.5f, label, style);
    to
    Code (csharp):
    1. PrefixLabel (totalPos, 0.5f, label, GUI.skin.label);
    and that should fix it:)
    Still a mess with the styles, especially in the editor playmode when a mix between editor and game GUI skin is shown, but anyway....
     
  35. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Hehe, no worries, i'm just teasing you. :p

    Great, work perfectly - just two places it needed to be fixed. :)
    Yeah, no comments from me about that... ;p
     
  36. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    ...and now I have a few more minor issues. Yay. :)

    1: How to remove/hide the "Adapt to screen" and make it always true? It's REALLY annoying having to click it. ;)

    2: With text fields I have to click two times before the cursor is active in that field. How to fix?

    3: How to change the background color of a node?
     
  37. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Don't be afraid to look into the code, there's not always a ready-to-use solution somewhere;)
    Just check in RTNodeEditor and disable the toggle in the OnGUI function and instead set the variable to true by default.

    Second, I think this might be related to a resorting bug of the GUI that isn't possible to fix very easy... sorry. Basically, some controls don't let the click event through so the node has first to be selected and then you can select the controls...

    For the node color, check out Node.DrawNode, you can change GUI.color to a property in it.
    This has been implemented in the develop branch, but a bit different - you can check it out here, it's using this method to change the background style:)
     
  38. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Heh, you know what? I actually HAVE been looking around just that one. :D But a var named screenSize doesn't sound like a true/false value to me... ;)

    Hmm...that's...bad. But thanks for telling me.

    Interesting. :) But..it uses ResourceManager aka Not Available During RunTimeThingy. Correct?
     
  39. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    @Jamsa78 Haha ok:) Well, is it really a 'var'? I usually don't use weak typing like that but explicit types such as bool for that exact reason... would surprise me if it was a 'var'.

    The only reason ResourceManager is even there is to unify access to the resources for both editor and runtime - so instead of having to differenciate between AssetDatabase and Resources in each occasion, you simply use ResourceManager which works out the correct path and API for you - can throw nearly any format at it (absolute, assets, resources) and it works in both editor and at runtime;) And it has some additional features like caching of tinted (knobs and other GUI elements) and otherwise modified textures (rotation of the knobs), which is the reason we access it in this case:)
     
  40. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Heh, well a mouseover on the OBJECT :D tells me it's a var-iable of the boolean type. Ok ok ok, it SAYS "Field", but isn't that just another name for a variable? :) Still a wierd name for a boolean! ;)

    Aha. I guess I've COMPLETELY misunderstood that ResourceManager! :D
     
  41. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Hmm...maybe I'm just thickheaded. (no comments please ;) ) but when in place, shouldn't I just write something like backgroundColor = Color.blue; in my node?
     
  42. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Sure, thought you meant browsing the code I was using the syntax 'var screenSpace;' or similar - which is a weak declaration where the type is interpreted by the compiler later on, which is IMO really annyoing. Anyway:)
    Well if you're using the develop branch, then yes. Else, just copy that to the master or switch - develop has lot's more features currently, but hasn't been signed of as stable yet although it pretty much is...
     
  43. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    It is declared properly. :) But the NAMING...! ;) ugh, don't get me started on type weak languages. :(((

    I copied it to my project, that I downloaded a few weeks ago (stable). But it doesn't change color, even though there's no compiler errors. I'm not going to get a fresh version (even if it's "mostly" stable ;) ) with loads of new stuff, when I'm not even halfway through learning this one! :D
     
  44. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Uhm...any comments on getting it to work in my project? :)

    I've put the AssureNodeBGStyle function and related vars/usings/functioncall in the Node.cs file.

    And the backgroundColor = Color.blue; in my node file.

    Do I need to do something else??
     
  45. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hey, sorry for the late reply, missed it somehow.
    Did you change the DrawNode funtion I linked to use that style for the node body/header?
     
  46. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Happens. :)

    Well, I'm not at the office, so I can't check. But I think that everything is as you told me to... ;)
     
    Seneral likes this.
  47. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Ok, now I'm at the office.

    This is the changes to my code in node.cs:
    Code (CSharp):
    1.  
    2.  
    3. using NodeEditorFramework.Utilities;
    4.  
    5.         public Color backgroundColor = Color.white;
    6.         private Color lastBGColor = Color.white;
    7.         private GUIStyle nodeBGStyle;
    8.  
    9. protected internal virtual void DrawNode ()
    10.         {
    11.             AssureNodeBGStyle();
    12.  
    13.             // TODO: Node Editor Feature: Custom Windowing System
    14.             // Create a rect that is adjusted to the editor zoom
    15.             Rect nodeRect = rect;
    16.             nodeRect.position += NodeEditor.curEditorState.zoomPanAdjust + NodeEditor.curEditorState.panOffset;
    17.             contentOffset = new Vector2 (0, 20);
    18.  
    19.             // Create a headerRect out of the previous rect and draw it, marking the selected node as such by making the header bold
    20.             Rect headerRect = new Rect (nodeRect.x, nodeRect.y, nodeRect.width, contentOffset.y);
    21.             GUI.Label (headerRect, name, NodeEditor.curEditorState.selectedNode == this? NodeEditorGUI.nodeBoxBold : NodeEditorGUI.nodeBox);
    22.  
    23.             // Begin the body frame around the NodeGUI
    24.             Rect bodyRect = new Rect (nodeRect.x, nodeRect.y + contentOffset.y, nodeRect.width, nodeRect.height - contentOffset.y);
    25.             GUI.BeginGroup (bodyRect, GUI.skin.box);
    26.             bodyRect.position = Vector2.zero;
    27.             GUILayout.BeginArea (bodyRect, GUI.skin.box);
    28.             // Call NodeGUI
    29.             GUI.changed = false;
    30.             NodeGUI ();
    31.             // End NodeGUI frame
    32.             GUILayout.EndArea ();
    33.             GUI.EndGroup ();
    34.         }
    35.  
    36.         private void AssureNodeBGStyle()
    37.         {
    38.             if (nodeBGStyle == null || nodeBGStyle.normal.background == null || lastBGColor != backgroundColor)
    39.             {
    40.                 lastBGColor = backgroundColor;
    41.                 nodeBGStyle = new GUIStyle(GUI.skin.box);
    42.                 nodeBGStyle.normal.background = ResourceManager.GetTintedTexture("Textures/NE_Box.png", backgroundColor);
    43.             }
    44.         }
    45.  

    This is my node:

    Code (CSharp):
    1. using UnityEngine;
    2. using NodeEditorFramework;
    3. using NodeEditorFramework.Utilities;
    4.  
    5. namespace NodeEditorFramework.Standard
    6. {
    7.     [Node(false, "HOP/Provide")]
    8.     public class ProvideNode : Node
    9.     {
    10.         public const string ID = "provideNode";
    11.         public override string GetID { get { return ID; } }
    12.  
    13.         public override bool AllowRecursion { get { return true; } }
    14.  
    15.         public float varValue = 1f;
    16.         public string varName = "";
    17.  
    18.         //public override Color backgroundColor = Color.white;
    19.  
    20.         public override Node Create(Vector2 pos)
    21.         {
    22.             ProvideNode node = CreateInstance<ProvideNode>();
    23.  
    24.             node.rect = new Rect(pos.x, pos.y, 150, 80);
    25.             node.name = "Provide";
    26.             node.backgroundColor = Color.blue;
    27.  
    28.             node.CreateInput(" ", "Float");
    29.             node.CreateInput(" ", "Float");
    30.             node.CreateOutput(" ", "Float");
    31.             node.CreateOutput(" ", "Float");
    32.  
    33.             return node;
    34.         }
    35.  
    36.         protected internal override void NodeGUI()
    37.         {
    38.             //GUILayout.Label("This is a Provide Node!");
    39.             backgroundColor = Color.blue;
    40.             RTEditorGUI.Space();
    41.             varName = RTEditorGUI.TextField(new GUIContent("Name", "The input value of type text"), varName, null);
    42.             varValue = RTEditorGUI.FloatField(new GUIContent("Value", "The input value of type float"), varValue);
    43.  
    44.             GUILayout.BeginHorizontal();
    45.             GUILayout.BeginVertical();
    46.  
    47.             Inputs[0].DisplayLayout();
    48.  
    49.             GUILayout.EndVertical();
    50.             GUILayout.BeginVertical();
    51.  
    52.             Outputs[0].DisplayLayout();
    53.  
    54.             GUILayout.EndVertical();
    55.             GUILayout.EndHorizontal();
    56.  
    57.         }
    58.  
    59.         public override bool Calculate()
    60.         {
    61.             if (!allInputsReady())
    62.                 return false;
    63.             Outputs[0].SetValue<float>(Inputs[0].GetValue<float>() * 5);
    64.             return true;
    65.         }
    66.     }
    67. }
    See anything wrong?? :)
     
  48. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    @Jamsa78 As I said, you missed to change DrawNode to actually use that style;)
     
  49. Jamsa78

    Jamsa78

    Joined:
    Mar 29, 2017
    Posts:
    96
    Heh, so I did. :D

    Is it supposed to only color the "bottom"-part, and not the headline?
     
  50. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    It is, but you can easily change that by adding other styles for the header;)