1
0
mirror of https://github.com/Siccity/xNode.git synced 2026-02-06 07:14:56 +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
guid: 84fbc8acdc9656941b529a16e8bbe318
timeCreated: 1506462197
guid: 2e27fbb85ccd5994e932fd8a6d34e4b3
timeCreated: 1506790922
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 11400000

View File

@ -1,9 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
/// <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 {
}

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 override void OnNodeGUI() {
base.OnNodeGUI();
public override void OnNodeGUI(out Dictionary<NodePort, Vector2> portPositions) {
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>
public class NodeEditor {
public Dictionary<NodePort, Rect> portRects = new Dictionary<NodePort, Rect>();
public Node target;
public virtual void OnNodeGUI() {
portRects.Clear();
DrawDefaultNodePortsGUI();
/// <summary> Draws the node GUI.</summary>
/// <param name="portPositions">Port handle positions need to be returned to the NodeEditorWindow </param>
public virtual void OnNodeGUI(out Dictionary<NodePort,Vector2> portPositions) {
DrawDefaultNodePortsGUI(out portPositions);
DrawDefaultNodeBodyGUI();
}
/// <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;
@ -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) {
/// <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;
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;
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) {

View File

@ -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) {

View File

@ -1,6 +1,7 @@
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
/// <summary> Contains GUI methods </summary>
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;
}
/// <summary> Draws all connections </summary>
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));
}
}
}
}
/// <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() {
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<NodePort, Vector2> 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);
}
}

View File

@ -7,15 +7,16 @@ using UnityEditor.Callbacks;
using System;
[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; } }
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(); } }
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();
/// <summary> Create editor window </summary>
@ -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<NodeGraph>(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;

View File

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