Creating a Node Based Editor in Unity

Node based editors are frequently used in game engines; Unity has Animator window, Unreal Engine has blueprint system, some other game engines have dialogue systems. In this blog post, we will create our own node based editor in Unity. It’s going to be simple node editor that you’ll be able to improve upon later (see the below image for an example). I am going to assume that you are already familiar with editor scripting, but if you are not, please take your time to have a look at this post and this post.

node-based-editor
We will be creating this window in this post (click to see the gif in action)

Creating the Window

Let’s start by creating a simple editor window. The structure of the code will be similar to that of the console clone we developed in the previous posts: draw the elements first, and then process input, and if the GUI is changed due to input events, force the window to repaint.

Drawing Nodes

Well, since this is a node editor, it should contain a list of nodes, which requires us to define a List<Node>. But first we should define Node class. A Node will be responsible for drawing itself and processing its own events. Unlike ProcessEvents(Event e) in NodeBasedEditor, ProcessEvents(Event e) in Node will return a boolean so that we can check whether we should repaint the GUI or not.

Creating Nodes

Nodes are drawn in the editor now, but we can’t see them if we don’t create them. We should display a context menu with an “Add node” item when the user right clicks in the editor. When the user clicks “Add node”, we will create a Node and add it to the list of nodes, so that it is drawn. A Node requires a position, a width, a height and a styling; position will be the mouse’s current position, width will be 200, height will be 50 (yes, I don’t like using magic numbers in code, but this is going to be a simple editor, so the size doesn’t matter for the moment) and we will replicate the Animator window’s node style for styling.

Making Nodes Draggable

Alright, now we are able to add nodes, but we can’t drag them around. As I mentioned earlier, nodes will be processing their own events, hence we will be handling drag event in Node class. One important thing to note here is that we should be “using” the drag event with Use() method. Later on, we will be adding canvas dragging, and we wouldn’t want to drag a node and the whole canvas at the same time (“using” an event prevents it being used by other processes, i.e. it stops event bubbling). Also note that the for loop in ProcessNodeEvents(Event e) traverses the node list backwards, because the last node is drawn at the top, so it should process the events first.

Creating Connections Between Nodes

Our node editor now has nodes, but we should also be able to connect them. In order to do this, we need two connection points (in and out) on a node and a connection between them. A connection point has a rectangle (so that we can draw it), has a type (in or out), has a style and it references its parent node. Therefore, our ConnectionPoint class will be a very simple one; drawing a button at a specific position and doing an action when this button is clicked.

On the other hand, a connection has two connection points and an action to remove it. Connection class is much simpler than ConnectionPoint, however, it introduces a new concept: Handles. This class is actually used to draw 3D GUI controls in the Scene view, but it is the only class with a bezier drawing method: Handles.DrawBezier(Vector3, Vector3, Vector3, Vector3, Color, Texture2D, float). It takes 7 parameters and the first 4 parameters are the position controls (start position, end position, start tangent, and end tangent), while the rest determine how the bezier looks.

Drawing Connections

Since the Connection and ConnectionPoint classes are ready, all we have to do is draw connection points in Node class and draw connections in NodeBasedEditor. Changes in Node class will be minimal; we will define two connection points, modify the constructor so that we can pass the styling and actions for them, and draw them in Draw() method.

However, NodeBasedEditor needs significant modification. First of all, we need to define styles for connection points. You can use a single style for both of them, but I would like them to look different, so I will be using separate styles for each. We are going to initialize these styles in OnEnable() just like we initialized node style.

Secondly, we need to keep track of clicked connection points, so that when a user selects an in and out, we should create a connection between them. This step includes most of the additions:

  • OnClickInPoint(ConnectionPoint) handles clicking an in point.
  • OnClickOutPoint(ConnectionPoint) handles clicking an out point.
  • OnClickRemoveConnection(Connection) handles clicking the remove button on connections.
  • CreateConnection() creates a connection when an in and an out point is selected.
  • ClearConnectionSelection() clears selected points.

And lastly, we need to draw connections in OnGUI() just like we draw nodes.

Selecting Nodes

We should provide feedback when the user clicks on a node so that they would know which node they selected (or if a node is selected at all). This is going to be useful when a user wants to remove a node.

Removing Nodes

Some node editors prefer to put the remove node button on the node itself, but in our case it might be dangerous: the user might remove a node accidentally. So, we are going to do the next best thing: put that button on a context menu. Users should select the node first and then right click on it in order to access the remove node button. When the user clicks remove node, we will remove the node from the nodes list. However, the node might have connections to other nodes, so we should remove those connections first.

Final Touches

The node editor is complete at this point, but it lacks some important features which would elevate the user experience:

  • A draggable canvas,
  • A bezier from selected connection point to the mouse position,
  • A grid in the background.

Making our canvas draggable is the easiest one, so let’s start with that. All we have to do is just apply mouse drag to every single node in the node list.

Next up, drawing the bezier from selected connection point to the mouse position. By drawing this bezier, we will let users know which connection point selected and how their connection will look like.

And finally, drawing the grid:

Conclusion

This concludes our tutorial and to be frank, our node editor looks like any first grade node editor you can find on the Asset Store. From this point on, you can work on it and create your own custom node editor. Here is a quest editor I built while working on a prototype:

sample-node-based-editor

And as always, here is the script in full, below. Until next time.