diff --git a/Examples/NodeToy/NodeGraphExample.asset b/Examples/NodeToy/NodeGraphExample.asset index d703c11..595fa1a 100644 Binary files a/Examples/NodeToy/NodeGraphExample.asset and b/Examples/NodeToy/NodeGraphExample.asset differ diff --git a/Examples/NodeToy/NodeGraphExample.asset.meta b/Examples/NodeToy/NodeGraphExample.asset.meta index e7e9fda..1af7b94 100644 --- a/Examples/NodeToy/NodeGraphExample.asset.meta +++ b/Examples/NodeToy/NodeGraphExample.asset.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 84fbc8acdc9656941b529a16e8bbe318 -timeCreated: 1506462197 +guid: 2e27fbb85ccd5994e932fd8a6d34e4b3 +timeCreated: 1506790922 licenseType: Free NativeFormatImporter: mainObjectFileID: 11400000 diff --git a/Examples/NodeToy/MathToyNodeGraph.cs b/Examples/NodeToy/NodeGraphExample.cs similarity index 62% rename from Examples/NodeToy/MathToyNodeGraph.cs rename to Examples/NodeToy/NodeGraphExample.cs index 8f89aa0..0d38514 100644 --- a/Examples/NodeToy/MathToyNodeGraph.cs +++ b/Examples/NodeToy/NodeGraphExample.cs @@ -1,9 +1,8 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; - +using System; /// Defines an example nodegraph. -[CreateAssetMenu(fileName = "NodeGraphExample", menuName = "Node Graph/Example")] +[Serializable, CreateAssetMenu(fileName = "NodeGraphExample", menuName = "Node Graph/Example")] public class NodeGraphExample : NodeGraph { - } diff --git a/Examples/NodeToy/MathToyNodeGraph.cs.meta b/Examples/NodeToy/NodeGraphExample.cs.meta similarity index 100% rename from Examples/NodeToy/MathToyNodeGraph.cs.meta rename to Examples/NodeToy/NodeGraphExample.cs.meta diff --git a/Examples/Nodes/Editor/MathNodeEditor.cs b/Examples/Nodes/Editor/MathNodeEditor.cs index 7dbc033..1cca50a 100644 --- a/Examples/Nodes/Editor/MathNodeEditor.cs +++ b/Examples/Nodes/Editor/MathNodeEditor.cs @@ -1,7 +1,10 @@ -[CustomNodeEditor(typeof(MathNode), "Math")] +using System.Collections.Generic; +using UnityEngine; + +[CustomNodeEditor(typeof(MathNode), "Math")] public class AddNodeEditor : NodeEditor { - public override void OnNodeGUI() { - base.OnNodeGUI(); + public override void OnNodeGUI(out Dictionary portPositions) { + base.OnNodeGUI(out portPositions); } } diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 6d825a6..312ec3c 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -8,17 +8,18 @@ using System; /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. public class NodeEditor { - public Dictionary portRects = new Dictionary(); public Node target; - public virtual void OnNodeGUI() { - portRects.Clear(); - DrawDefaultNodePortsGUI(); + /// Draws the node GUI. + /// Port handle positions need to be returned to the NodeEditorWindow + public virtual void OnNodeGUI(out Dictionary portPositions) { + DrawDefaultNodePortsGUI(out portPositions); DrawDefaultNodeBodyGUI(); } /// Draws standard editors for all fields marked with or - protected void DrawDefaultNodePortsGUI() { + protected void DrawDefaultNodePortsGUI(out Dictionary portPositions) { + portPositions = new Dictionary(); Event e = Event.current; @@ -27,14 +28,16 @@ public class NodeEditor { //Inputs GUILayout.BeginVertical(); for (int i = 0; i < target.InputCount; i++) { - DrawNodePortGUI(target.inputs[i]); + Vector2 handlePoint = DrawNodePortGUI(target.inputs[i]); + portPositions.Add(target.inputs[i], handlePoint); } GUILayout.EndVertical(); //Outputs GUILayout.BeginVertical(); for (int i = 0; i < target.OutputCount; i++) { - DrawNodePortGUI(target.outputs[i]); + Vector2 handlePoint = DrawNodePortGUI(target.outputs[i]); + portPositions.Add(target.outputs[i], handlePoint); } GUILayout.EndVertical(); @@ -52,31 +55,25 @@ public class NodeEditor { EditorGUILayout.Space(); } - protected void DrawNodePortGUI(NodePort port) { + /// Draw node port GUI using automatic layouting. Returns port handle position. + protected Vector2 DrawNodePortGUI(NodePort port) { GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name.PrettifyCamelCase()), style); - DrawNodePortGUI(rect, port); + return DrawNodePortGUI(rect, port); } - protected void DrawNodePortGUI(Rect rect, NodePort port) { + /// Draw node port GUI in rect. Returns port handle position. + protected Vector2 DrawNodePortGUI(Rect rect, NodePort port) { GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; GUI.Label(rect, new GUIContent(port.name.PrettifyCamelCase()), style); - Rect handleRect = new Rect(0, 0, 16, 16); + + Vector2 handlePoint = rect.center; + switch (port.direction) { - case NodePort.IO.Input: - handleRect.position = new Vector2(rect.xMin - 8, rect.position.y + (rect.height * 0.5f) - 8); - break; - case NodePort.IO.Output: - handleRect.position = new Vector2(rect.xMax - 8, rect.position.y + (rect.height * 0.5f) - 8); - break; + case NodePort.IO.Input: handlePoint.x = rect.xMin; break; + case NodePort.IO.Output: handlePoint.x = rect.xMax; break; } - portRects.Add(port, handleRect); - Color col = GUI.color; - GUI.color = NodeEditorUtilities.GetTypeColor(port.type); - GUI.DrawTexture(handleRect, NodeEditorResources.dot); - GUI.color = new Color(0.29f,0.31f,0.32f); - GUI.DrawTexture(handleRect, NodeEditorResources.dotOuter); - GUI.color = col; + return handlePoint; } private static FieldInfo[] GetInspectorFields(Node node) { diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 9c85df8..0868684 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -129,8 +129,8 @@ public partial class NodeEditorWindow { public void DrawDraggedConnection() { if (IsDraggingPort) { if (!_portConnectionPoints.ContainsKey(draggedOutput)) return; - Vector2 from = draggedOutput.node.rect.position + _portConnectionPoints[draggedOutput].center; - Vector2 to = draggedOutputTarget != null ? draggedOutputTarget.node.rect.position + portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); + Vector2 from = _portConnectionPoints[draggedOutput].center; + Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); Color col = NodeEditorUtilities.GetTypeColor(draggedOutput.type); col.a = 0.6f; DrawConnection(from, to, col); @@ -154,33 +154,29 @@ public partial class NodeEditorWindow { Repaint(); } //If we are hovering a node, check if we are also hovering a port - if (IsHoveringNode) { - NodePort newHoverPort = null; - //Check all input ports - for (int i = 0; i < hoveredNode.InputCount; i++) { - NodePort port = hoveredNode.inputs[i]; + NodePort newHoverPort = null; + //Check all input ports + for (int k = 0; k < graph.nodes.Count; k++) { + + for (int i = 0; i < graph.nodes[k].InputCount; i++) { + NodePort port = graph.nodes[k].inputs[i]; //Check if port rect is available if (!portConnectionPoints.ContainsKey(port)) continue; - Rect r = portConnectionPoints[port]; - r.position = GridToWindowPosition(r.position + hoveredNode.rect.position); - r.size /= zoom; + Rect r = GridToWindowRect(portConnectionPoints[port]); if (r.Contains(mousePos)) newHoverPort = port; } //Check all output ports - for (int i = 0; i < hoveredNode.OutputCount; i++) { - NodePort port = hoveredNode.outputs[i]; + for (int i = 0; i < graph.nodes[k].OutputCount; i++) { + NodePort port = graph.nodes[k].outputs[i]; //Check if port rect is available if (!portConnectionPoints.ContainsKey(port)) continue; - Rect r = portConnectionPoints[port]; - r.position = GridToWindowPosition(r.position + hoveredNode.rect.position); - r.size /= zoom; + Rect r = GridToWindowRect(portConnectionPoints[port]); if (r.Contains(mousePos)) newHoverPort = port; } - if (newHoverPort != hoveredPort) { - hoveredPort = newHoverPort; - } } - else hoveredPort = null; + if (newHoverPort != hoveredPort) { + hoveredPort = newHoverPort; + } } bool IsHoveringTitle(Node node) { diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index b52c26c..4aff684 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEditor; using System; +using System.Collections.Generic; /// Contains GUI methods public partial class NodeEditorWindow { @@ -14,6 +15,7 @@ public partial class NodeEditorWindow { DrawNodes(); DrawConnections(); DrawDraggedConnection(); + DrawPortHandles(); DrawToolbar(); GUI.matrix = m; @@ -117,6 +119,7 @@ public partial class NodeEditorWindow { GUI.color = prevCol; } + /// Draws all connections public void DrawConnections() { foreach (Node node in graph.nodes) { for (int i = 0; i < node.OutputCount; i++) { @@ -124,16 +127,30 @@ public partial class NodeEditorWindow { //Needs cleanup. Null checks are ugly if (!portConnectionPoints.ContainsKey(output)) continue; - Vector2 from = _portConnectionPoints[output].center + node.rect.position; + Vector2 from = _portConnectionPoints[output].center; for (int k = 0; k < output.ConnectionCount; k++) { NodePort input = output.GetConnection(k); - Vector2 to = input.node.rect.position + _portConnectionPoints[input].center; + Vector2 to = _portConnectionPoints[input].center; DrawConnection(from, to, NodeEditorUtilities.GetTypeColor(output.type)); } } } } + /// Draws the draggable circle handles on the ports + public void DrawPortHandles() { + Color col = GUI.color; + foreach(var kvp in portConnectionPoints) { + Rect rect = GridToWindowRect(kvp.Value); + GUI.color = new Color(0.29f, 0.31f, 0.32f); + GUI.DrawTexture(rect, NodeEditorResources.dotOuter); + GUI.color = NodeEditorUtilities.GetTypeColor(kvp.Key.type); + GUI.DrawTexture(rect, NodeEditorResources.dot); + + } + GUI.color = col; + } + private void DrawNodes() { Event e = Event.current; if (e.type == EventType.Repaint) portConnectionPoints.Clear(); @@ -164,10 +181,14 @@ public partial class NodeEditorWindow { nodeEditor.target = node; - nodeEditor.OnNodeGUI(); + Dictionary portHandlePoints; + nodeEditor.OnNodeGUI(out portHandlePoints); if (e.type == EventType.Repaint) { - foreach (var kvp in nodeEditor.portRects) { - portConnectionPoints.Add(kvp.Key, kvp.Value); + foreach (var kvp in portHandlePoints) { + Vector2 portHandlePos = kvp.Value; + portHandlePos += node.rect.position; + Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16); + portConnectionPoints.Add(kvp.Key, rect); } } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 13f1f5f..71bb640 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -7,15 +7,16 @@ using UnityEditor.Callbacks; using System; [InitializeOnLoad] -public partial class NodeEditorWindow : EditorWindow { +public partial class NodeEditorWindow : EditorWindow { + /// Stores node positions for all nodePorts. public Dictionary portConnectionPoints { get { return _portConnectionPoints; } } private Dictionary _portConnectionPoints = new Dictionary(); - public NodeGraph graph { get { return _graph != null ? _graph : _graph = CreateInstance(); } } - public NodeGraph _graph; + public NodeGraph graph; public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } } private Vector2 _panOffset; public float zoom { get { return _zoom; } set { _zoom = Mathf.Clamp(value, 1f, 5f); Repaint(); } } - private float _zoom = 1; + private float _zoom = 1; + partial void OnEnable(); /// Create editor window @@ -29,8 +30,8 @@ public partial class NodeEditorWindow : EditorWindow { } public void Save() { - if (AssetDatabase.Contains(_graph)) { - EditorUtility.SetDirty(_graph); + if (AssetDatabase.Contains(graph)) { + EditorUtility.SetDirty(graph); AssetDatabase.SaveAssets(); } else SaveAs(); @@ -42,8 +43,8 @@ public partial class NodeEditorWindow : EditorWindow { else { NodeGraph existingGraph = AssetDatabase.LoadAssetAtPath(path); if (existingGraph != null) AssetDatabase.DeleteAsset(path); - AssetDatabase.CreateAsset(_graph, path); - EditorUtility.SetDirty(_graph); + AssetDatabase.CreateAsset(graph, path); + EditorUtility.SetDirty(graph); AssetDatabase.SaveAssets(); } } @@ -60,6 +61,12 @@ public partial class NodeEditorWindow : EditorWindow { return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition/zoom); } + public Rect GridToWindowRect(Rect gridRect) { + gridRect.position = GridToWindowPosition(gridRect.position); + gridRect.size /= zoom; + return gridRect; + } + public Vector2 GridToWindowPositionNoClipped(Vector2 gridPosition) { Vector2 center = position.size * 0.5f; float xOffset = (center.x * zoom + (panOffset.x + gridPosition.x)); @@ -71,12 +78,13 @@ public partial class NodeEditorWindow : EditorWindow { selectedNode = node; } + [OnOpenAsset(0)] public static bool OnOpen(int instanceID, int line) { NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as NodeGraph; if (nodeGraph != null) { NodeEditorWindow w = Init(); - w._graph = nodeGraph; + w.graph = nodeGraph; return true; } return false; diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index c8cf56c..128add7 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -4,13 +4,11 @@ using UnityEngine; using System; /// Base class for all node graphs +[Serializable] public abstract class NodeGraph : ScriptableObject { /// All nodes in the graph. /// See: - [NonSerialized] public List nodes = new List(); - - /// Serialized nodes. - [SerializeField] public string[] s_nodes; + [SerializeField] public List nodes = new List(); public T AddNode() where T : Node { return AddNode(typeof(T)) as T;