1
0
mirror of https://github.com/Siccity/xNode.git synced 2026-02-14 02:58:45 +08:00

Connections now draw under port handles

Modulized code. Too many minor changes to address
This commit is contained in:
Thor Brigsted 2017-09-30 22:27:01 +02:00
parent f8a0bb8f7c
commit 6a92f18618
10 changed files with 91 additions and 69 deletions

View File

@ -1,6 +1,6 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 84fbc8acdc9656941b529a16e8bbe318 guid: 2e27fbb85ccd5994e932fd8a6d34e4b3
timeCreated: 1506462197 timeCreated: 1506790922
licenseType: Free licenseType: Free
NativeFormatImporter: NativeFormatImporter:
mainObjectFileID: 11400000 mainObjectFileID: 11400000

View File

@ -1,9 +1,8 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using System;
/// <summary> Defines an example nodegraph. </summary> /// <summary> Defines an example nodegraph. </summary>
[CreateAssetMenu(fileName = "NodeGraphExample", menuName = "Node Graph/Example")] [Serializable, CreateAssetMenu(fileName = "NodeGraphExample", menuName = "Node Graph/Example")]
public class NodeGraphExample : NodeGraph { public class NodeGraphExample : NodeGraph {
} }

View File

@ -1,7 +1,10 @@
[CustomNodeEditor(typeof(MathNode), "Math")] using System.Collections.Generic;
using UnityEngine;
[CustomNodeEditor(typeof(MathNode), "Math")]
public class AddNodeEditor : NodeEditor { public class AddNodeEditor : NodeEditor {
public override void OnNodeGUI() { public override void OnNodeGUI(out Dictionary<NodePort, Vector2> portPositions) {
base.OnNodeGUI(); base.OnNodeGUI(out portPositions);
} }
} }

View File

@ -8,17 +8,18 @@ using System;
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary> /// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
public class NodeEditor { public class NodeEditor {
public Dictionary<NodePort, Rect> portRects = new Dictionary<NodePort, Rect>();
public Node target; public Node target;
public virtual void OnNodeGUI() { /// <summary> Draws the node GUI.</summary>
portRects.Clear(); /// <param name="portPositions">Port handle positions need to be returned to the NodeEditorWindow </param>
DrawDefaultNodePortsGUI(); public virtual void OnNodeGUI(out Dictionary<NodePort,Vector2> portPositions) {
DrawDefaultNodePortsGUI(out portPositions);
DrawDefaultNodeBodyGUI(); DrawDefaultNodeBodyGUI();
} }
/// <summary> Draws standard editors for all fields marked with <see cref="Node.InputAttribute"/> or <see cref="Node.OutputAttribute"/> </summary> /// <summary> Draws standard editors for all fields marked with <see cref="Node.InputAttribute"/> or <see cref="Node.OutputAttribute"/> </summary>
protected void DrawDefaultNodePortsGUI() { protected void DrawDefaultNodePortsGUI(out Dictionary<NodePort, Vector2> portPositions) {
portPositions = new Dictionary<NodePort, Vector2>();
Event e = Event.current; Event e = Event.current;
@ -27,14 +28,16 @@ public class NodeEditor {
//Inputs //Inputs
GUILayout.BeginVertical(); GUILayout.BeginVertical();
for (int i = 0; i < target.InputCount; i++) { 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(); GUILayout.EndVertical();
//Outputs //Outputs
GUILayout.BeginVertical(); GUILayout.BeginVertical();
for (int i = 0; i < target.OutputCount; i++) { 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(); GUILayout.EndVertical();
@ -52,31 +55,25 @@ public class NodeEditor {
EditorGUILayout.Space(); EditorGUILayout.Space();
} }
protected void DrawNodePortGUI(NodePort port) { /// <summary> Draw node port GUI using automatic layouting. Returns port handle position. </summary>
protected Vector2 DrawNodePortGUI(NodePort port) {
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle;
Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name.PrettifyCamelCase()), style); Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name.PrettifyCamelCase()), style);
DrawNodePortGUI(rect, port); return DrawNodePortGUI(rect, port);
} }
protected void DrawNodePortGUI(Rect rect, NodePort port) { /// <summary> Draw node port GUI in rect. Returns port handle position. </summary>
protected Vector2 DrawNodePortGUI(Rect rect, NodePort port) {
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle;
GUI.Label(rect, new GUIContent(port.name.PrettifyCamelCase()), style); GUI.Label(rect, new GUIContent(port.name.PrettifyCamelCase()), style);
Rect handleRect = new Rect(0, 0, 16, 16);
Vector2 handlePoint = rect.center;
switch (port.direction) { switch (port.direction) {
case NodePort.IO.Input: case NodePort.IO.Input: handlePoint.x = rect.xMin; break;
handleRect.position = new Vector2(rect.xMin - 8, rect.position.y + (rect.height * 0.5f) - 8); case NodePort.IO.Output: handlePoint.x = rect.xMax; break;
break;
case NodePort.IO.Output:
handleRect.position = new Vector2(rect.xMax - 8, rect.position.y + (rect.height * 0.5f) - 8);
break;
} }
portRects.Add(port, handleRect); return handlePoint;
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;
} }
private static FieldInfo[] GetInspectorFields(Node node) { private static FieldInfo[] GetInspectorFields(Node node) {

View File

@ -129,8 +129,8 @@ public partial class NodeEditorWindow {
public void DrawDraggedConnection() { public void DrawDraggedConnection() {
if (IsDraggingPort) { if (IsDraggingPort) {
if (!_portConnectionPoints.ContainsKey(draggedOutput)) return; if (!_portConnectionPoints.ContainsKey(draggedOutput)) return;
Vector2 from = draggedOutput.node.rect.position + _portConnectionPoints[draggedOutput].center; Vector2 from = _portConnectionPoints[draggedOutput].center;
Vector2 to = draggedOutputTarget != null ? draggedOutputTarget.node.rect.position + portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition);
Color col = NodeEditorUtilities.GetTypeColor(draggedOutput.type); Color col = NodeEditorUtilities.GetTypeColor(draggedOutput.type);
col.a = 0.6f; col.a = 0.6f;
DrawConnection(from, to, col); DrawConnection(from, to, col);
@ -154,33 +154,29 @@ public partial class NodeEditorWindow {
Repaint(); Repaint();
} }
//If we are hovering a node, check if we are also hovering a port //If we are hovering a node, check if we are also hovering a port
if (IsHoveringNode) { NodePort newHoverPort = null;
NodePort newHoverPort = null; //Check all input ports
//Check all input ports for (int k = 0; k < graph.nodes.Count; k++) {
for (int i = 0; i < hoveredNode.InputCount; i++) {
NodePort port = hoveredNode.inputs[i]; for (int i = 0; i < graph.nodes[k].InputCount; i++) {
NodePort port = graph.nodes[k].inputs[i];
//Check if port rect is available //Check if port rect is available
if (!portConnectionPoints.ContainsKey(port)) continue; if (!portConnectionPoints.ContainsKey(port)) continue;
Rect r = portConnectionPoints[port]; Rect r = GridToWindowRect(portConnectionPoints[port]);
r.position = GridToWindowPosition(r.position + hoveredNode.rect.position);
r.size /= zoom;
if (r.Contains(mousePos)) newHoverPort = port; if (r.Contains(mousePos)) newHoverPort = port;
} }
//Check all output ports //Check all output ports
for (int i = 0; i < hoveredNode.OutputCount; i++) { for (int i = 0; i < graph.nodes[k].OutputCount; i++) {
NodePort port = hoveredNode.outputs[i]; NodePort port = graph.nodes[k].outputs[i];
//Check if port rect is available //Check if port rect is available
if (!portConnectionPoints.ContainsKey(port)) continue; if (!portConnectionPoints.ContainsKey(port)) continue;
Rect r = portConnectionPoints[port]; Rect r = GridToWindowRect(portConnectionPoints[port]);
r.position = GridToWindowPosition(r.position + hoveredNode.rect.position);
r.size /= zoom;
if (r.Contains(mousePos)) newHoverPort = port; if (r.Contains(mousePos)) newHoverPort = port;
} }
if (newHoverPort != hoveredPort) {
hoveredPort = newHoverPort;
}
} }
else hoveredPort = null; if (newHoverPort != hoveredPort) {
hoveredPort = newHoverPort;
}
} }
bool IsHoveringTitle(Node node) { bool IsHoveringTitle(Node node) {

View File

@ -1,6 +1,7 @@
using UnityEngine; using UnityEngine;
using UnityEditor; using UnityEditor;
using System; using System;
using System.Collections.Generic;
/// <summary> Contains GUI methods </summary> /// <summary> Contains GUI methods </summary>
public partial class NodeEditorWindow { public partial class NodeEditorWindow {
@ -14,6 +15,7 @@ public partial class NodeEditorWindow {
DrawNodes(); DrawNodes();
DrawConnections(); DrawConnections();
DrawDraggedConnection(); DrawDraggedConnection();
DrawPortHandles();
DrawToolbar(); DrawToolbar();
GUI.matrix = m; GUI.matrix = m;
@ -117,6 +119,7 @@ public partial class NodeEditorWindow {
GUI.color = prevCol; GUI.color = prevCol;
} }
/// <summary> Draws all connections </summary>
public void DrawConnections() { public void DrawConnections() {
foreach (Node node in graph.nodes) { foreach (Node node in graph.nodes) {
for (int i = 0; i < node.OutputCount; i++) { for (int i = 0; i < node.OutputCount; i++) {
@ -124,16 +127,30 @@ public partial class NodeEditorWindow {
//Needs cleanup. Null checks are ugly //Needs cleanup. Null checks are ugly
if (!portConnectionPoints.ContainsKey(output)) continue; 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++) { for (int k = 0; k < output.ConnectionCount; k++) {
NodePort input = output.GetConnection(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)); DrawConnection(from, to, NodeEditorUtilities.GetTypeColor(output.type));
} }
} }
} }
} }
/// <summary> Draws the draggable circle handles on the ports </summary>
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() { private void DrawNodes() {
Event e = Event.current; Event e = Event.current;
if (e.type == EventType.Repaint) portConnectionPoints.Clear(); if (e.type == EventType.Repaint) portConnectionPoints.Clear();
@ -164,10 +181,14 @@ public partial class NodeEditorWindow {
nodeEditor.target = node; nodeEditor.target = node;
nodeEditor.OnNodeGUI(); Dictionary<NodePort, Vector2> portHandlePoints;
nodeEditor.OnNodeGUI(out portHandlePoints);
if (e.type == EventType.Repaint) { if (e.type == EventType.Repaint) {
foreach (var kvp in nodeEditor.portRects) { foreach (var kvp in portHandlePoints) {
portConnectionPoints.Add(kvp.Key, kvp.Value); 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);
} }
} }

View File

@ -7,15 +7,16 @@ using UnityEditor.Callbacks;
using System; using System;
[InitializeOnLoad] [InitializeOnLoad]
public partial class NodeEditorWindow : EditorWindow { public partial class NodeEditorWindow : EditorWindow {
/// <summary> Stores node positions for all nodePorts. </summary>
public Dictionary<NodePort, Rect> portConnectionPoints { get { return _portConnectionPoints; } } public Dictionary<NodePort, Rect> portConnectionPoints { get { return _portConnectionPoints; } }
private Dictionary<NodePort, Rect> _portConnectionPoints = new Dictionary<NodePort, Rect>(); private Dictionary<NodePort, Rect> _portConnectionPoints = new Dictionary<NodePort, Rect>();
public NodeGraph graph { get { return _graph != null ? _graph : _graph = CreateInstance<NodeGraph>(); } } public NodeGraph graph;
public NodeGraph _graph;
public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } } public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } }
private Vector2 _panOffset; private Vector2 _panOffset;
public float zoom { get { return _zoom; } set { _zoom = Mathf.Clamp(value, 1f, 5f); Repaint(); } } 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(); partial void OnEnable();
/// <summary> Create editor window </summary> /// <summary> Create editor window </summary>
@ -29,8 +30,8 @@ public partial class NodeEditorWindow : EditorWindow {
} }
public void Save() { public void Save() {
if (AssetDatabase.Contains(_graph)) { if (AssetDatabase.Contains(graph)) {
EditorUtility.SetDirty(_graph); EditorUtility.SetDirty(graph);
AssetDatabase.SaveAssets(); AssetDatabase.SaveAssets();
} }
else SaveAs(); else SaveAs();
@ -42,8 +43,8 @@ public partial class NodeEditorWindow : EditorWindow {
else { else {
NodeGraph existingGraph = AssetDatabase.LoadAssetAtPath<NodeGraph>(path); NodeGraph existingGraph = AssetDatabase.LoadAssetAtPath<NodeGraph>(path);
if (existingGraph != null) AssetDatabase.DeleteAsset(path); if (existingGraph != null) AssetDatabase.DeleteAsset(path);
AssetDatabase.CreateAsset(_graph, path); AssetDatabase.CreateAsset(graph, path);
EditorUtility.SetDirty(_graph); EditorUtility.SetDirty(graph);
AssetDatabase.SaveAssets(); AssetDatabase.SaveAssets();
} }
} }
@ -60,6 +61,12 @@ public partial class NodeEditorWindow : EditorWindow {
return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition/zoom); 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) { public Vector2 GridToWindowPositionNoClipped(Vector2 gridPosition) {
Vector2 center = position.size * 0.5f; Vector2 center = position.size * 0.5f;
float xOffset = (center.x * zoom + (panOffset.x + gridPosition.x)); float xOffset = (center.x * zoom + (panOffset.x + gridPosition.x));
@ -71,12 +78,13 @@ public partial class NodeEditorWindow : EditorWindow {
selectedNode = node; selectedNode = node;
} }
[OnOpenAsset(0)] [OnOpenAsset(0)]
public static bool OnOpen(int instanceID, int line) { public static bool OnOpen(int instanceID, int line) {
NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as NodeGraph; NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as NodeGraph;
if (nodeGraph != null) { if (nodeGraph != null) {
NodeEditorWindow w = Init(); NodeEditorWindow w = Init();
w._graph = nodeGraph; w.graph = nodeGraph;
return true; return true;
} }
return false; return false;

View File

@ -4,13 +4,11 @@ using UnityEngine;
using System; using System;
/// <summary> Base class for all node graphs </summary> /// <summary> Base class for all node graphs </summary>
[Serializable]
public abstract class NodeGraph : ScriptableObject { public abstract class NodeGraph : ScriptableObject {
/// <summary> All nodes in the graph. <para/> /// <summary> All nodes in the graph. <para/>
/// See: <see cref="AddNode{T}"/> </summary> /// See: <see cref="AddNode{T}"/> </summary>
[NonSerialized] public List<Node> nodes = new List<Node>(); [SerializeField] public List<Node> nodes = new List<Node>();
/// <summary> Serialized nodes. </summary>
[SerializeField] public string[] s_nodes;
public T AddNode<T>() where T : Node { public T AddNode<T>() where T : Node {
return AddNode(typeof(T)) as T; return AddNode(typeof(T)) as T;