Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Simple node editor

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

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Are single Out connections broken in the development branch? I can get nodes working just fine with multi out and with single/multi in, but the following results in an out node that doesn't produce anything:
    Code (csharp):
    1. [ConnectionKnob("Node Out", Direction.Out, ConnectionCount.Single, NodeSide.Right, 10)]
     
  2. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hi!
    Well, they're not exactly broken. To be exact the whole editing never really adapted to the new knob options available...

    Previously, only Multi-Single connections where possible, so it was fixed that you created your connection from multi to single and deleted from single, because that provided an easy way to edit the connections.

    As for single-single, I forgot to adapt. What you now have to add is the option to draw new connections from a single-knob when it is not connected (and still allow to delete it when a connection IS available).
    This is a quick fix though. In NodeEditorInputControls.cs, region Connection, in HandleConnectionDrawing, add the else statement:
    Code (csharp):
    1. else if (state.focusedConnectionKnob.maxConnectionCount == ConnectionCount.Single)
    2. { // Knob with single connection clicked
    3.     if (state.focusedConnectionKnob.connected())
    4.     { // Loose and edit existing connection from it
    5.         state.connectKnob = state.focusedConnectionKnob.connection(0);
    6.         state.focusedConnectionKnob.RemoveConnection(state.connectKnob);
    7.         inputInfo.inputEvent.Use();
    8.     }
    9.     else
    10.     { // Not connected, draw a new connection from it
    11.         state.connectKnob = state.focusedConnectionKnob;
    12.         inputInfo.inputEvent.Use();
    13.     }
    14. }
    That should do it:)

    As for multi-multi, their currently known to be not supported. There are concepts on how to edit multi-multi connections but I've not gotten around to implement them yet. It would require a revamp of the connection editing even for existing multi-single connections though, but it's better.
     
  3. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    That's super-helpful, thank you! :)
     
  4. PedroDuran

    PedroDuran

    Joined:
    Aug 19, 2014
    Posts:
    30
    Captura.PNG I finally figured out on the zoom and panning thing ..

    I ended up re writing all the node editor code but now it is more clean.. ( i still working on the new code .. actually i just made some "tests" nodes for testing, now it's time to integrate it to the behavior tree itself... )

    and i changed from my styles to the build in unity's mecanim styles that i think they look pretty cool.

    Apart of the zoom and panning thing now it support multi selection nodes.. :)
     
  5. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    315
    I was wondering if there is any way to use the RTCanvasCalculator to run calculations but also to load the canvas being calculated in the node editor to observe the nodes being calculated?

    I tried doing something like this:
    Code (CSharp):
    1. NodeEditorWindow editor = NodeEditorWindow.OpenNodeEditor ();
    2. editor.canvasCache.SetCanvas(canvas);
    where "canvas" is a reference to the canvas that being calculated by RTCanvasCalculator, but the window opens up a solid gray (not even the grid is drawn). If I don't call SetCanvas() I get a blank canvas. Is this possible?
     
  6. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hm, that should work - apart from the fact that you can't access editor scripts and windows from a monobehaviour. Maybe you did something (Like moving files) to fix that which breaks paths and causes the node editor initialization to fail? If there was an error with the loaded canvas or setup, it would just create a new canvas, so that seems most likely to me:)
     
  7. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    315
    Hi Seneral,

    Thanks for the reply. I setup a simple reproduction case in case it helps. First off, here is what I see (including warnings and such):
    https://imgur.com/a/d7PZj

    I had my warnings turned off earlier so I wasn't seeing these. My apologies for not providing that information in my previous post. Anyway, to reproduce this, all I had to do was edit RTCanvasCaluclatorEditor.cs and change the OnInspectorGUI() function to open the editor and set the canvas like this:
    Code (CSharp):
    1. if (GUILayout.Button ("Calculate and debug Output")) {
    2.     //RTCalc.CalculateCanvas ();
    3.     NodeEditorWindow editor = NodeEditorWindow.OpenNodeEditor ();
    4.     editor.canvasCache.SetCanvas(RTCalc.canvas);
    5. }
    I assigned the "Calculation Canvas.asset" to the RTCanvasCalculator in the editor. Ran the scene, and clicked the button in the inspector. This should work?

    I am using Unity 2017.2.0f3 if it matters.

    I did have the plugin in a non-default path, but I had reflected that with the "editorPath" variable. I also tried reverting that path and moving everything into the default path of "/Assets/Plugins/Node_Editor/".
     
    Last edited: Dec 18, 2017
  8. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    @gtzpower Oh sure forgot about the custom editor. Will try again, I'm gone currently, but from my memory I would say that's the correct way of doing this... maybe it intercepts a bit because you're not doing a working-copy of the canvas but directly set it, but in that case that is probably a bug...

    EDIT: Ok yes took a look at this. It's the cache system trying to create a backup cache asset, but failing because the canvas you passed to SetCanvas is already saved as an asset, so as a result a working copy is created. That not only breaks connection to the canvas in the RTCanvasCalculator but also seems to create another error, which results in the blank window. To fix that I need to add support to NodeEditorUserCache (only the wrapper around the framework for holding the canvas) to be able to disable the cache system for a given loaded canvas...
    Seneral
     
    Last edited: Dec 18, 2017
  9. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    315
    Thanks again for the response. This is not holding me up on my end; I just want it for debugging purposes so no rush.

    I think I may have found another issue though. Not sure if I am just misinterpreting the meaning of the function or if the logic is backwards. In Node.cs the following function is returning true when I would expect it to return false:
    Code (CSharp):
    1. /// <summary>
    2. /// Returns whether the node acts as an input (no inputs or no inputs assigned)
    3. /// </summary>
    4. public bool isInput()
    5. {
    6.     for (int i = 0; i < inputPorts.Count; i++)
    7.         if (!inputPorts[i].connected())
    8.             return false;
    9.     return true;
    10. }
    11.  
    I assumed based on the description that if the input port is connected, then this function should return FALSE that the node is not input node. I think the conditional needs to have the "!" operator removed to make this work (or am I misunderstanding the purpose of this function?). The same issue looks like it could be a problem on the "isOutput()" function.

    I ran into this by trying to copy CanvasCalculator traversal methods which I expected to get the first node and calculate all of the children by traversing the output ports. Instead, it was finding all nodes when building the work list, not just the ones that have no inputs.

    I am happy to address this and submit a pull request if that is easier for you. I am just not sure if this is by design or not.

    Thanks for all of your hard work! :rolleyes:
     
  10. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ah, yep you're right! Thanks very much for spotting that:)
    Yep if you want, I would appreciate a pull request - would do it myself tomorrow if not, no problem:)
    Seneral
     
  11. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    315
    pull request submitted
     
  12. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Thanks, merged:)
     
  13. maldewar

    maldewar

    Joined:
    Dec 21, 2016
    Posts:
    47
    First of all I want to thank you all for this wonderful node editor!
    I've been using it for the development of an editor extension to generate trees. I've come across a question related to the UI, hope you can help me with this matter.

    As I've been adding background textures and icons to nodes, these textures are showing at the "select texture dialog" on the editor. Now, I would prefer not to clutter other projects using this extension with unnecessary or unrelated texture on this dialog.

    Is there any way to hide textures from the dialog displayed when selecting a texture?

     
  14. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Interesting question, unfortunately I don't know of a built-in way to do that with the default texture picker right now, would love if you found a solution for that...
    If you have a controlled, limited set of textures to choose from (like a specific folder) you might be able to create a custom GUI control in the node property editor to display a dropdown/texture pads to select from after reading those in.
    There's also the in-between way of just using a file open dialogue but it's also a bit annoying for the user.
    So, it depends on exactly which textures you want to show... In case you do need to make a custom control, as I said, you can just use the property editor (overwrite DrawNodePropertyEditor, and use base.DrawNodePropertyEditor) to inject that into the inspector GUI if you don't want to create a custom editor and mess with the default node custom editor...
    Hope that makes sense:)
    Seneral
     
    antoripa likes this.
  15. maldewar

    maldewar

    Joined:
    Dec 21, 2016
    Posts:
    47
    Thanks Seneral!
    Yes, I'm overriding DrawNodePropertyEditor on some nodes that need to display some extra information. As for the textures, I've opted for a single sprite image for the iconography (so the texture dialog would show only this for the extension UI). I'm cropping the required images and saving the Texture2D on memory, just taking care of listening to the editor events to clean or reload things when necessary.
     
    Seneral likes this.
  16. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Cool, so you did get it to work?
     
  17. maldewar

    maldewar

    Joined:
    Dec 21, 2016
    Posts:
    47
    Yes, thanks! Although it was working before, I just wanted to make things more tidy :)
     
  18. GeekZebra

    GeekZebra

    Joined:
    Jan 28, 2013
    Posts:
    22
    TaleOf4Gamers, D3Duck and Seneral like this.
  19. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    @toinouc Wow looks nice on first glance:) Especially like the concept of warm/cold inputs, although hard to grasp at first...
     
    GeekZebra likes this.
  20. GeekZebra

    GeekZebra

    Joined:
    Jan 28, 2013
    Posts:
    22
    Hi @Seneral ,
    Thanks for your feedback :), I will try to improve the doc, it's not very clear on many aspects. I'm thinking about making a video tutorial to explain the basics of the language. Some abstracts aspects will be easier to understand if they are visualized before they are explained.
     
  21. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Agreed - although I'm (obviously) not one for visual programming languages, it's a really cool start you got there:)
     
    GeekZebra likes this.
  22. bjornrun

    bjornrun

    Joined:
    Oct 29, 2013
    Posts:
    88
  23. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Depending on where these UnityEditor references are. In the base framework there should be none, and the default nodes all use RTEditorGUI as replacement controls for the missing editor controls.
    If you have custom nodes you either have to use RTEditorGUI aswell or wrap all editor references in #if UNITY_EDITOR / #endif to make it build :)
    So for me, using the develop branch and the examples, I had to do nothing special to make it build, just switch to WebGL platform, setup a scene with RTNodeEditor and some saved scene canvases to load from, and build it...
    Seneral
     
    bjornrun likes this.
  24. bjornrun

    bjornrun

    Joined:
    Oct 29, 2013
    Posts:
    88
    Works perfectly. But now I want buttons and extra text fields in runtime, and the Examples/Dialogue-System looks promising. This is not working in runtime, and I'm not sure how to proceed.
     
  25. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    You mean the editor or the runtime with the actual dialogue?
    If you want the editor at runtime you need to add support for it yourself, currently not designed for it. I recommend replacing every EditorGUI with RTEditorGUI if existing or work around it somehow, on a case-by-case basis... No better solution there...
     
  26. bjornrun

    bjornrun

    Joined:
    Oct 29, 2013
    Posts:
    88
    What needs to be implemented is a runtime version of EditorGUILayout. Perhaps I just expand the RTEditorGUI with the missing UI components or place it outside the framework.

    To support directionless connections, I had to change the code in node.cs: DrawConnections() to iterate all connectionPorts and not just outputPorts, to see the connections drawn. I guess it would be better to create a new class of connections that can be both input and output, and not be called None as it is now.
     
  27. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    That draws the connection twice, once from each node it is connected to. Seems though as if I didn't test it enough, sorry!

    But yes, you're right about RTEditorGUI, would love if you can contribute some more components, it really is a pity that some of the basic controla are only available in the editor...
     
  28. bjornrun

    bjornrun

    Joined:
    Oct 29, 2013
    Posts:
    88
    I would like to save and load from WebGL player. I guess it should be export to XML and then import. But the current export/import code in develop doesn't really work in WebGL runtime. Still it almost does, so do you have any ideas regarding load and save in WebGL?
     
  29. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    I recommend you to read through some guides regarding file I/O on WebGL or web in general. As you probably can assume it's a difficult topic because of safety concerns, but there are ways to do that as far as I know.
    Won't do that natively in the framework as it's an absolute edge case, but provided you get file IO in general running, changing the import/export functions should be a breeze:)
    Seneral
     
  30. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    28
    Hi!
    I have a simple requirement from this Node Editor Framework.
    Currently, I work on a system that uses tree's (data structure) as input data,
    and I want to use visual node editor to show Tree visually.

    I easily managed to create a simple nodes with inputs/outputs, however, I got stuck with the creation of Tree structure. I cannot figure out how to acces children of the node, or which nodes are connected with ConnectionKnob.

    I tried using ValueConnectionKnob.

    Code (CSharp):
    1. [ValueConnectionKnob(null, Direction.Out, "BoardNode", ConnectionCount.Multi)]
    2.         public ValueConnectionKnob children;
    Code (CSharp):
    1. public class BoardNodeConnectionType : ValueConnectionType
    2.     {
    3.         public override string Identifier { get { return "BoardNode"; } }
    4.         public override Color Color { get { return Color.cyan; } }
    5.         public override System.Type Type { get { return typeof(List<BaseNode>); } }
    6.     }
    Code (CSharp):
    1. public override void NodeGUI()
    2.         {
    3.             List<BoardNode> childs = GetChildren();
    4.             if (childs != null)
    5.                 RTEditorGUI.TextField("Number of children: " + childs.Count);
    6.  
    7.             if (GUI.changed)
    8.                 NodeEditor.curNodeCanvas.OnNodeChange(this);
    9.         }
    10.  
    11.  
    12.         public virtual List<BoardNode> GetChildren()
    13.         {
    14.             if (children.connected())
    15.                 return children.GetValue<List<BoardNode>>();
    16.             return null;
    17.         }
    18. public override bool Calculate()
    19.         {
    20.             if (parents.connected())
    21.             {
    22.                 List<BaseNode> val = parents.GetValue<List<BaseNode>>();
    23.                 if (val == null)
    24.                     val = new List<BaseNode>();
    25.                 if (!val.Contains(this))
    26.                     val.Add(this);
    27.                 parents.SetValue(val);
    28.             }
    29.             return true;
    30.         }
    However this result's with an error
    Code (CSharp):
    1. Trying to GetValue<System.Collections.Generic.List`1[[NodeEditorFramework.BoardNodeTree.BoardNode, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]> for Output Type: System.Collections.Generic.List`1[[NodeEditorFramework.BoardNodeTree.BaseNode, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
     
  31. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    The method GetValue is a method of ValueConnectionKnob which is used to pass an object/value down a Connection. If you want to get the connected nodes, use children.connections.Select(k => k.body);
    Generally, if you only edit your data with the graph, or need custom traversal, I recommend to create a custom canvas type und traversal routine (provided you are using the develop branch).
    Seneral
     
    markoal likes this.
  32. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    28
    Thanks for the advice, I will check out how to implement custom canvas type.
    Is there maybe some examples of creating custom canvas or some kind of documentation for it?
     
  33. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Yes, as I said, provided you use the develop branch and not master.
    There isn't much to it actually, there is a section in the docs for it but it also mainly refers to the examples for now. The default calculation canvas and routines aswell as the graph canvas (both located in NodeEditor/Default) are implemented using this system... it basically allows you to script canvas behaviour.
    Seneral
     
    markoal likes this.
  34. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    28
    Hi,
    I got another question.

    I have extended NodeCanvas with my own and I got the method GetBoardTree(). I use a direct reference to the saved asset (canvas).
    Everything is working when NodeEditor is active, I change nodes, save and it returns valid BoardTree.
    Problem is when NodeEditor isn't active, it doesn't calculate BoardTree using connectionPort.body as it should.
    Since NodeCanvas is ScriptableObject I guessed it may work.
    Do you maybe have an idea to fix this issue?
     
  35. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    I assume you mean you have a reference to a Node in the custom NodeCanvas?
    If yes, then that is expected. In order to save the canvas, the framework (and any editor working on ScriptableObjects really) has to create copies of them (due to how Unity works). This is called a working copy in this framework. Unfortunately this breaks references when they are not updated.
    So in this framework I implemented these pair functions in all major framework objects (Node, NodeCanvas, ...):
    Code (csharp):
    1. /// <summary>
    2. /// Should return all additional ScriptableObjects this Node references
    3. /// </summary>
    4. public virtual ScriptableObject[] GetScriptableObjects () { return new ScriptableObject[0]; }
    5. /// <summary>
    6. /// Replaces all references to any ScriptableObjects this Node holds with the cloned versions in the serialization process.
    7. /// </summary>
    8. protected internal virtual void CopyScriptableObjects (System.Func<ScriptableObject, ScriptableObject> replaceSO) {}
    You can override them to register and update own scriptableObjects. In your case, the referenced node is already cloned, so no need to register it with GetScriptableObjects. You only need to use CopyScriptableObjects like this:
    nodeRef = replaceSO (nodeRef);
    and the reference will be update to the cloned node:)
    Seneral
     
    arvzg and markoal like this.
  36. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    28
    Thank you for the unexpected quick answer.
    So to be exact, I currently have BoardManager class that holds the reference to BoardTreeCanvas asset.
    Code (CSharp):
    1. public class BoardManager : MonoBehaviour
    2. {
    3.   public BoardTreeCanvas Tree;
    4.   public void GenerateBoard()
    5.   {
    6.     BoardNodeData = Tree.GetBoardTree();
    7.     //...some logic...
    8.   }
    9. }
    BoardManager calls BoardTreeCanvas (not node) which CreatesTree using it's nodes.
    I guess problem is that these nodes aren't referenced one to each other?
    Code (CSharp):
    1. public class BoardTreeCanvas : NodeCanvas
    2. {
    3.   public BoardNodeData GetBoardTree()
    4.   {
    5.      return CreateTree();
    6.   }
    7.   protected BoardNodeData CreateTree(){
    8.    // Finding root node (node without parents)
    9.    // Iterating over root children nodes using connectionPort
    10.    // Populating custom BoardNodeData (for each node we create BoardNodeData)
    11.    // Returning BoardNodeData of root
    12.   }
    13. }
    And BoardNodeData is some simple class.
    Code (CSharp):
    1. [System.Serializable]
    2. public class BoardNodeData
    3. {
    4. public List<BoardNodeData>children;
    5. public List<BoardNodeData>parents;
    6. // ... some custom data ..
    7. }
    What I thought before is that SO of canvas holds all data inside like any simple SO with custom data, where there are no references and I just read data, however, it isn't the case.
    Since I'm not familiar fully with the framework, I'm not sure how would help
    nodeRef = replaceSO (nodeRef);
    Should I use it for all node references in Canvas.nodes (if they are referenced here at all)?
    Should I implement replaceSO function myself?
    What should I replace in replaceSO method?
    I have found some similar logic in the NodeEditorSaveManager, looks like CreateWorkingCopy is something that I could use, but also it seems like overkill for something simple as just read data from existing SO.
     
  37. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ok so now I'm just confused lol
    Completely different from what I got from your first post... Anyway, still don't understand what exactly your problem is - I now know what you want to do, but what is hindering you?
    You should indeed just be able to do what you said - forget about the two functions I posted, since I misunderstood your problem with a different common error.
    You should have no problem creating an own data structure from within your custom canvas using the nodes list and the connections to determine the hierarchy...
     
    markoal likes this.
  38. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    28
    Sorry, I wasn't clear enough.
    So the problem is in the creation of board tree. Firstly when I try to find root node I do this:
    Code (CSharp):
    1.             List<Node> rootNodes = new List<Node>();
    2.             foreach (Node node in nodes)
    3.             {
    4.                 bool containInput = false;
    5.                 foreach (ConnectionPort inputPort in node.inputPorts)
    6.                 {
    7.                     containInput = inputPort.connections.Count > 0;
    8.                     if (containInput) break;
    9.                 }
    10.                 if (!containInput)
    11.                     rootNodes.Add(node);
    12.             }
    But, since i dont have active NodeEditor, ConnectionPorts in node.inputPorts are null (I guess this is where the reference logic we duscussed before comes).
    Currently, one of the solutions I could think of is to have direct references to Parents and Children nodes inside each Node.
     
  39. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ah ok makes sense.
    No, that is unrelated to the cloning stuff.
    In the newest develop branch, a concept of static and dynamic nodes have been introduced, aswell as a property-based ConnectionPort. What that means is that the concept of 'input' and 'output' is only a property on the same class, and the only lists in the node that are actually saved are the staticConnectionPorts and the dynamicConnectionPorts.
    The others are filled based on property on demand and are not serialized.

    I did not consider this yet unfortunately, sorry about that.
    So for directly referencing a canvas outside of the Editor, you need to call the following on each node you want to use the lists in:
    Code (csharp):
    1. NodeEditorFramework.ConnectionPortManager.UpdateRepresentativePortLists(node);
    It's really just filling the temporary lists. I'll make an automated process that simplifies this, maybe even completely erases the need to manually call that function!

    Seneral
     
    markoal likes this.
  40. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Hey @Seneral I want to add the ability to drag and drop assets from the Project view into the Node Editor window which turns into a Node.

    For example, if I drag a scene file into the node editor window, while I'm still keeping my mouse button clicked, when I mouse over the Node Editor window a new Node is created, one of the fields would be automatically filled with that scene file, and the new Node continues to be dragged around in the Editor window until I let go of the mouse button.

    There is a DragAndDrop class in UnityEditor which seems to be useful here, but I'm not quite sure where to add the code.

    Any pointers?
     
  41. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hm interesting - never really done something with DragAndDrop, but it should be possible.
    It seems you need to use the designated drag events and the DragAndDrop class you linked.

    First, use the the dynamic input handling system to receive the events.
    It works by applying a custom attribute to a static method somewhere in your project, see this as reference. Here are the attributes you'd need to catch the drag events:
    Code (csharp):
    1. [EventHandlerAttribute (EventType.DragUpdated)]
    2. [EventHandlerAttribute (EventType.DragPerform)]
    3. [EventHandlerAttribute (EventType.DragExited)]
    Either apply all to one method and use if or split them onto different methods, your choice. See the docs for more info on implementation.

    Then, you should be able to notice a drag into the canvas with the DragUpdated event. Verify it's a scene object with DragAndDrop, then create a 'temporary' node and assign your scene object to a field. See CreateNodeCallback (l. 30) as a reference for that:
    Code (csharp):
    1. inputInfo.SetAsCurrentEnvironment ();
    2. Node.Create ("MySceneNodeID", NodeEditor.ScreenToCanvasSpace (inputInfo.inputPos));
    Either store the temporary node as a static reference in the class with your methods or add it as a field to NodeEditorState. Everytime DragUpdated is issued, either create the temporary node or update it's position through that reference with node.position.
    Remember to use 'NodeEditor.ScreenToCanvasSpace' to account for the canvas space!

    Now, when DragExited is issued, you can easily delete it, and when DragPerform is issued, you can discard the temporary reference and treat the node as created.
    Sounds doable to me right now, but it wont be easy. Heard of some problems with the position being weird for the DragAndDrop...

    Hope that helps! Be sure to tell me if that works for you.
    Seneral
     
  42. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    @Seneral Thanks for the tips!

    I was able to get some basic functionality working:



    Here's the code so far: https://pastebin.com/DfbbdM0D

    Next up is probably moving the code from DragPerform to DragUpdate so I can achieve the automatic dragging after creation.

    I've added all the code in NodeEditorWindow.cs but this doesn't seem optimal, ideally I would want different Drag and Drop code support in each NodeCanvas, however I can't add this code to my NodeCanvas type as SceneAsset is in UnityEditor namespace only. Any suggestions for where all this code should go for canvas-specific implementation?

    I'm also getting this error in the console though after the Node is created:
    https://pastebin.com/MzYKCVa5

    In RTEditorGUI.cs:
    Code (CSharp):
    1. return GUILayoutUtility.GetRect (getFieldWidth() + minLabelW + 5, getFieldWidth() + maxLabelW + 5, textFieldHeight, textFieldHeight, options);
    Not sure how to fix that
     

    Attached Files:

    Last edited: Jan 31, 2018
  43. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Alright, I got it mostly working now!



    Code here: https://hastebin.com/icokazejut.cs

    Would like to position it so the mouse pointer is in the middle of the node rather than being on the top left

    Still would like get your thoughts on how best to seperate the drag-and-drop implementation based on different NodeCanvas types rather than it being in NodeEditorWindow universally for all canvases.

    And still getting that error, seems to be caused by my overridden NodeGUI() function on the line:

    Code (CSharp):
    1. someText = RTEditorGUI.TextField(new GUIContent("Some Text", "The Text"), someText);
    But only happens on the first time NodeGUI() is called, subsequent calls don't result in the error
     

    Attached Files:

    Last edited: Jan 31, 2018
  44. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Cool, looks good! :)
    To use the center, just substract node.size/2 from the assigned position.

    You can put that code snippet anywhere in your project, doesn't matter. The event system fetches the methods after compilation. I recommend to make a new static class for scene drag support, and do canvas/node specific stuff either using conditionals or any other way.

    As for the error, seems you can't add new GUI controls in the Drag events. The IMGUI system has the events Layout and Repaint, and if something was added in between, it shows this harmless error. So you might want to use a flag to mark you want to create a node, call repaint, and do it in the next repaint event? Just an idea...

    Seneral
     
  45. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Hmm, I see. So I suppose I'd just put in a new editor script and check if the current canvas type is my canvas and only run the code if it is.

    OR I could just put it in my node canvas type class, and use #if UNITY_EDITOR
     
  46. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Ya sure, but that would not switch behaviour with different canvas types. The methods are global, it does not matter whether they are placed in a specific canvas type or not.
    Don't know the specifics of what you are planning, but I'm glad it works:)
    Seneral
     
  47. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    @Seneral Last question! (hopefully)

    I want to put a field in my custom node that accepts a SceneAsset (so I can reference a Scene) basically like this:

    upload_2018-2-1_11-50-17.png

    This is done using EditorGUILayout.ObjectField(..) and is working fine in editor - however since SceneAsset is in UnityEditor namespace, my code won't compile.

    In the Inspector you get around this easily by separating the class from the editor (using CustomEditor). Is there some way to seperate a custom Node class from its editor code or some other way around this?

    Edit: I also noticed the Dialog System Example uses lots of UnityEditor stuff in all of the custom Node classes, and this also won't compile. How are you meant to use that?

    Edit: Well I managed to get around it simply by surrounding all the editor code in #if UNITY_EDITOR. It's a bit dirty, but it works. Let me know if you have any other suggestions!
     
    Last edited: Feb 1, 2018
  48. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    @arvzg Yes, use the #if UNITY_EDITOR preprocessor directive, although not fancy with lots of code to isolate...
    No other way currently.
    Seneral
     
    antoripa likes this.
  49. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Is AutoLayout currently broken for NodeSide.Bottom? I'm getting this:



    As you can see the 'Level Start' node has NodeSide.Bottom on its ValueConnectionKnob. I've set a MinSize of 200,10

    If I switch it to NodeSide.Right everything goes back to normal:

    upload_2018-2-1_16-27-56.png


    Edit: Another question @Seneral (sorry to bombard you with so much and thanks for all your help!) If I have a member variable in my custom Node class that's a reference to another Node, I noticed that on reload (either a recompile or a scene load) that the variable loses its data. If it's not a class (int, float, etc) then the data holds just fine.

    I've a feeling it has something to do with the way ScriptableObjects are serialized? I've tried setting my custom node to dirty and setting the hideflags to HideAndDontSave but it won't work in Play mode
     

    Attached Files:

    Last edited: Feb 2, 2018
  50. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Hm, weird, I'll check into that! It should only use the space the controls request. How do you position the knob? Any call like knob.DisplayLayout in the GUI?

    Regarding the node reference, yes it's known, related to SOs. I made a post explaining it just recently, it's basically the same:)