mirror of
https://github.com/Siccity/xNode.git
synced 2025-12-21 01:36:03 +08:00
Merge branch 'master' of github.com:Siccity/xNode into examples
This commit is contained in:
commit
5892bd0c45
@ -21,6 +21,10 @@ With a minimal footprint, it is ideal as a base for custom state machines, dialo
|
|||||||
* Does not rely on any 3rd party plugins
|
* Does not rely on any 3rd party plugins
|
||||||
* Custom node inspector code is very similar to regular custom inspector code
|
* Custom node inspector code is very similar to regular custom inspector code
|
||||||
|
|
||||||
|
### Wiki
|
||||||
|
* [Getting started](https://github.com/Siccity/xNode/wiki/Getting%20Started) - create your very first node node and graph
|
||||||
|
* [Examples branch](https://github.com/Siccity/xNode/tree/examples) - look at other small projects
|
||||||
|
|
||||||
### Node example:
|
### Node example:
|
||||||
```csharp
|
```csharp
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace XNodeEditor {
|
|||||||
/// <summary> Fires every whenever a node was modified through the editor </summary>
|
/// <summary> Fires every whenever a node was modified through the editor </summary>
|
||||||
public static Action<XNode.Node> onUpdateNode;
|
public static Action<XNode.Node> onUpdateNode;
|
||||||
public static Dictionary<XNode.NodePort, Vector2> portPositions;
|
public static Dictionary<XNode.NodePort, Vector2> portPositions;
|
||||||
|
public static int renaming;
|
||||||
|
|
||||||
/// <summary> Draws the node GUI.</summary>
|
/// <summary> Draws the node GUI.</summary>
|
||||||
/// <param name="portPositions">Port handle positions need to be returned to the NodeEditorWindow </param>
|
/// <param name="portPositions">Port handle positions need to be returned to the NodeEditorWindow </param>
|
||||||
@ -24,7 +25,21 @@ namespace XNodeEditor {
|
|||||||
public virtual void OnHeaderGUI() {
|
public virtual void OnHeaderGUI() {
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
string title = target.name;
|
string title = target.name;
|
||||||
GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
|
if (renaming != 0 && Selection.Contains(target)) {
|
||||||
|
int controlID = EditorGUIUtility.GetControlID(FocusType.Keyboard) + 1;
|
||||||
|
if (renaming == 1) {
|
||||||
|
EditorGUIUtility.keyboardControl = controlID;
|
||||||
|
EditorGUIUtility.editingTextField = true;
|
||||||
|
renaming = 2;
|
||||||
|
}
|
||||||
|
target.name = EditorGUILayout.TextField(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
|
||||||
|
if (!EditorGUIUtility.editingTextField) {
|
||||||
|
Rename(target.name);
|
||||||
|
renaming = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Draws standard field editors for all public fields </summary>
|
/// <summary> Draws standard field editors for all public fields </summary>
|
||||||
@ -52,6 +67,15 @@ namespace XNodeEditor {
|
|||||||
else return Color.white;
|
else return Color.white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitiateRename() {
|
||||||
|
renaming = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rename(string newName) {
|
||||||
|
target.name = newName;
|
||||||
|
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
||||||
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class CustomNodeEditorAttribute : Attribute,
|
public class CustomNodeEditorAttribute : Attribute,
|
||||||
XNodeEditor.Internal.NodeEditorBase<NodeEditor, NodeEditor.CustomNodeEditorAttribute, XNode.Node>.INodeEditorAttrib {
|
XNodeEditor.Internal.NodeEditorBase<NodeEditor, NodeEditor.CustomNodeEditorAttribute, XNode.Node>.INodeEditorAttrib {
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
public partial class NodeEditorWindow {
|
public partial class NodeEditorWindow {
|
||||||
public enum NodeActivity { Idle, HoldHeader, DragHeader, HoldGrid, DragGrid }
|
public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid }
|
||||||
public static NodeActivity currentActivity = NodeActivity.Idle;
|
public static NodeActivity currentActivity = NodeActivity.Idle;
|
||||||
public static bool isPanning { get; private set; }
|
public static bool isPanning { get; private set; }
|
||||||
public static Vector2[] dragOffset;
|
public static Vector2[] dragOffset;
|
||||||
@ -13,13 +14,36 @@ namespace XNodeEditor {
|
|||||||
private bool IsDraggingPort { get { return draggedOutput != null; } }
|
private bool IsDraggingPort { get { return draggedOutput != null; } }
|
||||||
private bool IsHoveringPort { get { return hoveredPort != null; } }
|
private bool IsHoveringPort { get { return hoveredPort != null; } }
|
||||||
private bool IsHoveringNode { get { return hoveredNode != null; } }
|
private bool IsHoveringNode { get { return hoveredNode != null; } }
|
||||||
|
private bool IsHoveringReroute { get { return hoveredReroute.port != null; } }
|
||||||
private XNode.Node hoveredNode = null;
|
private XNode.Node hoveredNode = null;
|
||||||
[NonSerialized] private XNode.NodePort hoveredPort = null;
|
[NonSerialized] private XNode.NodePort hoveredPort = null;
|
||||||
[NonSerialized] private XNode.NodePort draggedOutput = null;
|
[NonSerialized] private XNode.NodePort draggedOutput = null;
|
||||||
[NonSerialized] private XNode.NodePort draggedOutputTarget = null;
|
[NonSerialized] private XNode.NodePort draggedOutputTarget = null;
|
||||||
|
[NonSerialized] private List<Vector2> draggedOutputReroutes = new List<Vector2>();
|
||||||
|
private RerouteReference hoveredReroute = new RerouteReference();
|
||||||
|
private List<RerouteReference> selectedReroutes = new List<RerouteReference>();
|
||||||
private Rect nodeRects;
|
private Rect nodeRects;
|
||||||
private Vector2 dragBoxStart;
|
private Vector2 dragBoxStart;
|
||||||
private UnityEngine.Object[] preBoxSelection;
|
private UnityEngine.Object[] preBoxSelection;
|
||||||
|
private RerouteReference[] preBoxSelectionReroute;
|
||||||
|
private Rect selectionBox;
|
||||||
|
|
||||||
|
private struct RerouteReference {
|
||||||
|
public XNode.NodePort port;
|
||||||
|
public int connectionIndex;
|
||||||
|
public int pointIndex;
|
||||||
|
|
||||||
|
public RerouteReference(XNode.NodePort port, int connectionIndex, int pointIndex) {
|
||||||
|
this.port = port;
|
||||||
|
this.connectionIndex = connectionIndex;
|
||||||
|
this.pointIndex = pointIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex).Insert(pointIndex, pos); }
|
||||||
|
public void SetPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex) [pointIndex] = pos; }
|
||||||
|
public void RemovePoint() { port.GetReroutePoints(connectionIndex).RemoveAt(pointIndex); }
|
||||||
|
public Vector2 GetPoint() { return port.GetReroutePoints(connectionIndex) [pointIndex]; }
|
||||||
|
}
|
||||||
|
|
||||||
public void Controls() {
|
public void Controls() {
|
||||||
wantsMouseMove = true;
|
wantsMouseMove = true;
|
||||||
@ -42,32 +66,52 @@ namespace XNodeEditor {
|
|||||||
draggedOutputTarget = null;
|
draggedOutputTarget = null;
|
||||||
}
|
}
|
||||||
Repaint();
|
Repaint();
|
||||||
} else if (currentActivity == NodeActivity.HoldHeader || currentActivity == NodeActivity.DragHeader) {
|
} else if (currentActivity == NodeActivity.HoldNode) {
|
||||||
|
RecalculateDragOffsets(e);
|
||||||
|
currentActivity = NodeActivity.DragNode;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
if (currentActivity == NodeActivity.DragNode) {
|
||||||
|
// Holding ctrl inverts grid snap
|
||||||
|
bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap;
|
||||||
|
if (e.control) gridSnap = !gridSnap;
|
||||||
|
|
||||||
|
Vector2 mousePos = WindowToGridPosition(e.mousePosition);
|
||||||
|
// Move selected nodes with offset
|
||||||
for (int i = 0; i < Selection.objects.Length; i++) {
|
for (int i = 0; i < Selection.objects.Length; i++) {
|
||||||
if (Selection.objects[i] is XNode.Node) {
|
if (Selection.objects[i] is XNode.Node) {
|
||||||
XNode.Node node = Selection.objects[i] as XNode.Node;
|
XNode.Node node = Selection.objects[i] as XNode.Node;
|
||||||
node.position = WindowToGridPosition(e.mousePosition) + dragOffset[i];
|
node.position = mousePos + dragOffset[i];
|
||||||
bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap;
|
|
||||||
if (e.control) {
|
|
||||||
gridSnap = !gridSnap;
|
|
||||||
}
|
|
||||||
if (gridSnap) {
|
if (gridSnap) {
|
||||||
node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8;
|
node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8;
|
||||||
node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8;
|
node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentActivity = NodeActivity.DragHeader;
|
// Move selected reroutes with offset
|
||||||
|
for (int i = 0; i < selectedReroutes.Count; i++) {
|
||||||
|
Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i];
|
||||||
|
pos.x -= 8;
|
||||||
|
pos.y -= 8;
|
||||||
|
if (gridSnap) {
|
||||||
|
pos.x = (Mathf.Round((pos.x + 8) / 16) * 16);
|
||||||
|
pos.y = (Mathf.Round((pos.y + 8) / 16) * 16);
|
||||||
|
}
|
||||||
|
selectedReroutes[i].SetPoint(pos);
|
||||||
|
}
|
||||||
Repaint();
|
Repaint();
|
||||||
} else if (currentActivity == NodeActivity.HoldGrid) {
|
} else if (currentActivity == NodeActivity.HoldGrid) {
|
||||||
currentActivity = NodeActivity.DragGrid;
|
currentActivity = NodeActivity.DragGrid;
|
||||||
preBoxSelection = Selection.objects;
|
preBoxSelection = Selection.objects;
|
||||||
|
preBoxSelectionReroute = selectedReroutes.ToArray();
|
||||||
dragBoxStart = WindowToGridPosition(e.mousePosition);
|
dragBoxStart = WindowToGridPosition(e.mousePosition);
|
||||||
Repaint();
|
Repaint();
|
||||||
} else if (currentActivity == NodeActivity.DragGrid) {
|
} else if (currentActivity == NodeActivity.DragGrid) {
|
||||||
foreach (XNode.Node node in graph.nodes) {
|
Vector2 boxStartPos = GridToWindowPosition(dragBoxStart);
|
||||||
|
Vector2 boxSize = e.mousePosition - boxStartPos;
|
||||||
}
|
if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); }
|
||||||
|
if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); }
|
||||||
|
selectionBox = new Rect(boxStartPos, boxSize);
|
||||||
Repaint();
|
Repaint();
|
||||||
}
|
}
|
||||||
} else if (e.button == 1 || e.button == 2) {
|
} else if (e.button == 1 || e.button == 2) {
|
||||||
@ -83,6 +127,7 @@ namespace XNodeEditor {
|
|||||||
case EventType.MouseDown:
|
case EventType.MouseDown:
|
||||||
Repaint();
|
Repaint();
|
||||||
if (e.button == 0) {
|
if (e.button == 0) {
|
||||||
|
draggedOutputReroutes.Clear();
|
||||||
|
|
||||||
if (IsHoveringPort) {
|
if (IsHoveringPort) {
|
||||||
if (hoveredPort.IsOutput) {
|
if (hoveredPort.IsOutput) {
|
||||||
@ -92,6 +137,8 @@ namespace XNodeEditor {
|
|||||||
if (hoveredPort.IsConnected) {
|
if (hoveredPort.IsConnected) {
|
||||||
XNode.Node node = hoveredPort.node;
|
XNode.Node node = hoveredPort.node;
|
||||||
XNode.NodePort output = hoveredPort.Connection;
|
XNode.NodePort output = hoveredPort.Connection;
|
||||||
|
int outputConnectionIndex = output.GetConnectionIndex(hoveredPort);
|
||||||
|
draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex);
|
||||||
hoveredPort.Disconnect(output);
|
hoveredPort.Disconnect(output);
|
||||||
draggedOutput = output;
|
draggedOutput = output;
|
||||||
draggedOutputTarget = hoveredPort;
|
draggedOutputTarget = hoveredPort;
|
||||||
@ -100,22 +147,36 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
||||||
// If mousedown on node header, select or deselect
|
// If mousedown on node header, select or deselect
|
||||||
if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, e.control || e.shift);
|
if (!Selection.Contains(hoveredNode)) {
|
||||||
else if (e.control || e.shift) DeselectNode(hoveredNode);
|
SelectNode(hoveredNode, e.control || e.shift);
|
||||||
|
if (!e.control && !e.shift) selectedReroutes.Clear();
|
||||||
|
} else if (e.control || e.shift) DeselectNode(hoveredNode);
|
||||||
e.Use();
|
e.Use();
|
||||||
currentActivity = NodeActivity.HoldHeader;
|
currentActivity = NodeActivity.HoldNode;
|
||||||
dragOffset = new Vector2[Selection.objects.Length];
|
} else if (IsHoveringReroute) {
|
||||||
for (int i = 0; i < dragOffset.Length; i++) {
|
// If reroute isn't selected
|
||||||
if (Selection.objects[i] is XNode.Node) {
|
if (!selectedReroutes.Contains(hoveredReroute)) {
|
||||||
XNode.Node node = Selection.objects[i] as XNode.Node;
|
// Add it
|
||||||
dragOffset[i] = node.position - WindowToGridPosition(e.mousePosition);
|
if (e.control || e.shift) selectedReroutes.Add(hoveredReroute);
|
||||||
|
// Select it
|
||||||
|
else {
|
||||||
|
selectedReroutes = new List<RerouteReference>() { hoveredReroute };
|
||||||
|
Selection.activeObject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// Deselect
|
||||||
|
else if (e.control || e.shift) selectedReroutes.Remove(hoveredReroute);
|
||||||
|
e.Use();
|
||||||
|
currentActivity = NodeActivity.HoldNode;
|
||||||
}
|
}
|
||||||
// If mousedown on grid background, deselect all
|
// If mousedown on grid background, deselect all
|
||||||
else if (!IsHoveringNode) {
|
else if (!IsHoveringNode) {
|
||||||
currentActivity = NodeActivity.HoldGrid;
|
currentActivity = NodeActivity.HoldGrid;
|
||||||
if (!e.control && !e.shift) Selection.activeObject = null;
|
if (!e.control && !e.shift) {
|
||||||
|
selectedReroutes.Clear();
|
||||||
|
Selection.activeObject = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -127,16 +188,22 @@ namespace XNodeEditor {
|
|||||||
if (draggedOutputTarget != null) {
|
if (draggedOutputTarget != null) {
|
||||||
XNode.Node node = draggedOutputTarget.node;
|
XNode.Node node = draggedOutputTarget.node;
|
||||||
if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
|
if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
|
||||||
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
|
||||||
EditorUtility.SetDirty(graph);
|
// ConnectionIndex can be -1 if the connection is removed instantly after creation
|
||||||
|
int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
|
||||||
|
if (connectionIndex != -1) {
|
||||||
|
draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
|
||||||
|
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
||||||
|
EditorUtility.SetDirty(graph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//Release dragged connection
|
//Release dragged connection
|
||||||
draggedOutput = null;
|
draggedOutput = null;
|
||||||
draggedOutputTarget = null;
|
draggedOutputTarget = null;
|
||||||
EditorUtility.SetDirty(graph);
|
EditorUtility.SetDirty(graph);
|
||||||
AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
} else if (currentActivity == NodeActivity.DragHeader) {
|
} else if (currentActivity == NodeActivity.DragNode) {
|
||||||
AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
} else if (!IsHoveringNode) {
|
} else if (!IsHoveringNode) {
|
||||||
// If click outside node, release field focus
|
// If click outside node, release field focus
|
||||||
if (!isPanning) {
|
if (!isPanning) {
|
||||||
@ -147,19 +214,35 @@ namespace XNodeEditor {
|
|||||||
EditorGUIUtility.keyboardControl = 0;
|
EditorGUIUtility.keyboardControl = 0;
|
||||||
EditorGUIUtility.hotControl = 0;
|
EditorGUIUtility.hotControl = 0;
|
||||||
}
|
}
|
||||||
AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If click node header, select single node.
|
// If click node header, select it.
|
||||||
if (currentActivity == NodeActivity.HoldHeader && !(e.control || e.shift)) {
|
if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) {
|
||||||
|
selectedReroutes.Clear();
|
||||||
SelectNode(hoveredNode, false);
|
SelectNode(hoveredNode, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If click reroute, select it.
|
||||||
|
if (IsHoveringReroute && !(e.control || e.shift)) {
|
||||||
|
selectedReroutes = new List<RerouteReference>() { hoveredReroute };
|
||||||
|
Selection.activeObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
Repaint();
|
Repaint();
|
||||||
currentActivity = NodeActivity.Idle;
|
currentActivity = NodeActivity.Idle;
|
||||||
} else if (e.button == 1) {
|
} else if (e.button == 1) {
|
||||||
if (!isPanning) {
|
if (!isPanning) {
|
||||||
if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
if (IsDraggingPort) {
|
||||||
|
draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
|
||||||
|
} else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) {
|
||||||
|
selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
|
||||||
|
selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1);
|
||||||
|
} else if (IsHoveringReroute) {
|
||||||
|
ShowRerouteContextMenu(hoveredReroute);
|
||||||
|
} else if (IsHoveringPort) {
|
||||||
|
ShowPortContextMenu(hoveredPort);
|
||||||
|
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
||||||
if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false);
|
if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false);
|
||||||
ShowNodeContextMenu();
|
ShowNodeContextMenu();
|
||||||
} else if (!IsHoveringNode) {
|
} else if (!IsHoveringNode) {
|
||||||
@ -188,6 +271,22 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RecalculateDragOffsets(Event current) {
|
||||||
|
dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count];
|
||||||
|
// Selected nodes
|
||||||
|
for (int i = 0; i < Selection.objects.Length; i++) {
|
||||||
|
if (Selection.objects[i] is XNode.Node) {
|
||||||
|
XNode.Node node = Selection.objects[i] as XNode.Node;
|
||||||
|
dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected reroutes
|
||||||
|
for (int i = 0; i < selectedReroutes.Count; i++) {
|
||||||
|
dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Puts all nodes in focus. If no nodes are present, resets view to </summary>
|
/// <summary> Puts all nodes in focus. If no nodes are present, resets view to </summary>
|
||||||
public void Home() {
|
public void Home() {
|
||||||
zoom = 2;
|
zoom = 2;
|
||||||
@ -197,19 +296,37 @@ namespace XNodeEditor {
|
|||||||
public void CreateNode(Type type, Vector2 position) {
|
public void CreateNode(Type type, Vector2 position) {
|
||||||
XNode.Node node = graph.AddNode(type);
|
XNode.Node node = graph.AddNode(type);
|
||||||
node.position = position;
|
node.position = position;
|
||||||
|
node.name = UnityEditor.ObjectNames.NicifyVariableName(type.ToString());
|
||||||
|
AssetDatabase.AddObjectToAsset(node, graph);
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
Repaint();
|
Repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Remove nodes in the graph in Selection.objects</summary>
|
/// <summary> Remove nodes in the graph in Selection.objects</summary>
|
||||||
public void RemoveSelectedNodes() {
|
public void RemoveSelectedNodes() {
|
||||||
|
// We need to delete reroutes starting at the highest point index to avoid shifting indices
|
||||||
|
selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList();
|
||||||
|
for (int i = 0; i < selectedReroutes.Count; i++) {
|
||||||
|
selectedReroutes[i].RemovePoint();
|
||||||
|
}
|
||||||
|
selectedReroutes.Clear();
|
||||||
foreach (UnityEngine.Object item in Selection.objects) {
|
foreach (UnityEngine.Object item in Selection.objects) {
|
||||||
if (item is XNode.Node) {
|
if (item is XNode.Node) {
|
||||||
XNode.Node node = item as XNode.Node;
|
XNode.Node node = item as XNode.Node;
|
||||||
graph.RemoveNode(node);
|
graphEditor.RemoveNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary>
|
||||||
|
public void MoveNodeToTop(XNode.Node node) {
|
||||||
|
int index;
|
||||||
|
while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) {
|
||||||
|
graph.nodes[index] = graph.nodes[index + 1];
|
||||||
|
graph.nodes[index + 1] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Dublicate selected nodes and select the dublicates </summary>
|
/// <summary> Dublicate selected nodes and select the dublicates </summary>
|
||||||
public void DublicateSelectedNodes() {
|
public void DublicateSelectedNodes() {
|
||||||
UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length];
|
UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length];
|
||||||
@ -218,7 +335,7 @@ namespace XNodeEditor {
|
|||||||
if (Selection.objects[i] is XNode.Node) {
|
if (Selection.objects[i] is XNode.Node) {
|
||||||
XNode.Node srcNode = Selection.objects[i] as XNode.Node;
|
XNode.Node srcNode = Selection.objects[i] as XNode.Node;
|
||||||
if (srcNode.graph != graph) continue; // ignore nodes selected in another graph
|
if (srcNode.graph != graph) continue; // ignore nodes selected in another graph
|
||||||
XNode.Node newNode = graph.CopyNode(srcNode);
|
XNode.Node newNode = graphEditor.CopyNode(srcNode);
|
||||||
substitutes.Add(srcNode, newNode);
|
substitutes.Add(srcNode, newNode);
|
||||||
newNode.position = srcNode.position + new Vector2(30, 30);
|
newNode.position = srcNode.position + new Vector2(30, 30);
|
||||||
newNodes[i] = newNode;
|
newNodes[i] = newNode;
|
||||||
@ -254,12 +371,34 @@ namespace XNodeEditor {
|
|||||||
/// <summary> Draw a connection as we are dragging it </summary>
|
/// <summary> Draw a connection as we are dragging it </summary>
|
||||||
public void DrawDraggedConnection() {
|
public void DrawDraggedConnection() {
|
||||||
if (IsDraggingPort) {
|
if (IsDraggingPort) {
|
||||||
if (!_portConnectionPoints.ContainsKey(draggedOutput)) return;
|
|
||||||
Vector2 from = _portConnectionPoints[draggedOutput].center;
|
|
||||||
Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition);
|
|
||||||
Color col = NodeEditorPreferences.GetTypeColor(draggedOutput.ValueType);
|
Color col = NodeEditorPreferences.GetTypeColor(draggedOutput.ValueType);
|
||||||
|
|
||||||
|
if (!_portConnectionPoints.ContainsKey(draggedOutput)) return;
|
||||||
col.a = 0.6f;
|
col.a = 0.6f;
|
||||||
|
Vector2 from = _portConnectionPoints[draggedOutput].center;
|
||||||
|
Vector2 to = Vector2.zero;
|
||||||
|
for (int i = 0; i < draggedOutputReroutes.Count; i++) {
|
||||||
|
to = draggedOutputReroutes[i];
|
||||||
|
DrawConnection(from, to, col);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition);
|
||||||
DrawConnection(from, to, col);
|
DrawConnection(from, to, col);
|
||||||
|
|
||||||
|
Color bgcol = Color.black;
|
||||||
|
Color frcol = col;
|
||||||
|
bgcol.a = 0.6f;
|
||||||
|
frcol.a = 0.6f;
|
||||||
|
|
||||||
|
// Loop through reroute points again and draw the points
|
||||||
|
for (int i = 0; i < draggedOutputReroutes.Count; i++) {
|
||||||
|
// Draw reroute point at position
|
||||||
|
Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16));
|
||||||
|
rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8);
|
||||||
|
rect = GridToWindowRect(rect);
|
||||||
|
|
||||||
|
NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace XNodeEditor {
|
|||||||
// Check script type. Return if deleting a non-node script
|
// Check script type. Return if deleting a non-node script
|
||||||
UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
|
UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
|
||||||
System.Type scriptType = script.GetClass ();
|
System.Type scriptType = script.GetClass ();
|
||||||
if (scriptType != typeof (XNode.Node) && !scriptType.IsSubclassOf (typeof (XNode.Node))) return AssetDeleteResult.DidNotDelete;
|
if (scriptType == null || (scriptType != typeof (XNode.Node) && !scriptType.IsSubclassOf (typeof (XNode.Node)))) return AssetDeleteResult.DidNotDelete;
|
||||||
|
|
||||||
// Find all ScriptableObjects using this script
|
// Find all ScriptableObjects using this script
|
||||||
string[] guids = AssetDatabase.FindAssets ("t:" + scriptType);
|
string[] guids = AssetDatabase.FindAssets ("t:" + scriptType);
|
||||||
|
|||||||
@ -14,6 +14,7 @@ namespace XNodeEditor {
|
|||||||
Matrix4x4 m = GUI.matrix;
|
Matrix4x4 m = GUI.matrix;
|
||||||
if (graph == null) return;
|
if (graph == null) return;
|
||||||
graphEditor = NodeGraphEditor.GetEditor(graph);
|
graphEditor = NodeGraphEditor.GetEditor(graph);
|
||||||
|
graphEditor.position = position;
|
||||||
|
|
||||||
Controls();
|
Controls();
|
||||||
|
|
||||||
@ -21,8 +22,9 @@ namespace XNodeEditor {
|
|||||||
DrawConnections();
|
DrawConnections();
|
||||||
DrawDraggedConnection();
|
DrawDraggedConnection();
|
||||||
DrawNodes();
|
DrawNodes();
|
||||||
DrawBox();
|
DrawSelectionBox();
|
||||||
DrawTooltip();
|
DrawTooltip();
|
||||||
|
graphEditor.OnGUI();
|
||||||
|
|
||||||
GUI.matrix = m;
|
GUI.matrix = m;
|
||||||
}
|
}
|
||||||
@ -72,7 +74,7 @@ namespace XNodeEditor {
|
|||||||
GUI.DrawTextureWithTexCoords(rect, crossTex, new Rect(tileOffset + new Vector2(0.5f, 0.5f), tileAmount));
|
GUI.DrawTextureWithTexCoords(rect, crossTex, new Rect(tileOffset + new Vector2(0.5f, 0.5f), tileAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawBox() {
|
public void DrawSelectionBox() {
|
||||||
if (currentActivity == NodeActivity.DragGrid) {
|
if (currentActivity == NodeActivity.DragGrid) {
|
||||||
Vector2 curPos = WindowToGridPosition(Event.current.mousePosition);
|
Vector2 curPos = WindowToGridPosition(Event.current.mousePosition);
|
||||||
Vector2 size = curPos - dragBoxStart;
|
Vector2 size = curPos - dragBoxStart;
|
||||||
@ -87,19 +89,30 @@ namespace XNodeEditor {
|
|||||||
return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width));
|
return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Show right-click context menu for hovered reroute </summary>
|
||||||
|
void ShowRerouteContextMenu(RerouteReference reroute) {
|
||||||
|
GenericMenu contextMenu = new GenericMenu();
|
||||||
|
contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint());
|
||||||
|
contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Show right-click context menu for hovered port </summary>
|
||||||
|
void ShowPortContextMenu(XNode.NodePort hoveredPort) {
|
||||||
|
GenericMenu contextMenu = new GenericMenu();
|
||||||
|
contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections());
|
||||||
|
contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Show right-click context menu for selected nodes </summary>
|
/// <summary> Show right-click context menu for selected nodes </summary>
|
||||||
public void ShowNodeContextMenu() {
|
public void ShowNodeContextMenu() {
|
||||||
GenericMenu contextMenu = new GenericMenu();
|
GenericMenu contextMenu = new GenericMenu();
|
||||||
// If only one node is selected
|
// If only one node is selected
|
||||||
if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
|
if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
|
||||||
XNode.Node node = Selection.activeObject as XNode.Node;
|
XNode.Node node = Selection.activeObject as XNode.Node;
|
||||||
contextMenu.AddItem(new GUIContent("Move To Top"), false, () => {
|
contextMenu.AddItem(new GUIContent("Move To Top"), false, () => MoveNodeToTop(node));
|
||||||
int index;
|
contextMenu.AddItem(new GUIContent("Rename"), false, NodeEditor.GetEditor(node).InitiateRename);
|
||||||
while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) {
|
|
||||||
graph.nodes[index] = graph.nodes[index + 1];
|
|
||||||
graph.nodes[index + 1] = node;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contextMenu.AddItem(new GUIContent("Duplicate"), false, DublicateSelectedNodes);
|
contextMenu.AddItem(new GUIContent("Duplicate"), false, DublicateSelectedNodes);
|
||||||
@ -199,26 +212,67 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
/// <summary> Draws all connections </summary>
|
/// <summary> Draws all connections </summary>
|
||||||
public void DrawConnections() {
|
public void DrawConnections() {
|
||||||
|
Vector2 mousePos = Event.current.mousePosition;
|
||||||
|
List<RerouteReference> selection = preBoxSelectionReroute != null ? new List<RerouteReference>(preBoxSelectionReroute) : new List<RerouteReference>();
|
||||||
|
hoveredReroute = new RerouteReference();
|
||||||
|
|
||||||
|
Color col = GUI.color;
|
||||||
foreach (XNode.Node node in graph.nodes) {
|
foreach (XNode.Node node in graph.nodes) {
|
||||||
//If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset.
|
//If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset.
|
||||||
if (node == null) continue;
|
if (node == null) continue;
|
||||||
|
|
||||||
|
// Draw full connections and output > reroute
|
||||||
foreach (XNode.NodePort output in node.Outputs) {
|
foreach (XNode.NodePort output in node.Outputs) {
|
||||||
//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;
|
|
||||||
for (int k = 0; k < output.ConnectionCount; k++) {
|
|
||||||
|
|
||||||
|
Color connectionColor = graphEditor.GetTypeColor(output.ValueType);
|
||||||
|
|
||||||
|
for (int k = 0; k < output.ConnectionCount; k++) {
|
||||||
XNode.NodePort input = output.GetConnection(k);
|
XNode.NodePort input = output.GetConnection(k);
|
||||||
|
|
||||||
|
// Error handling
|
||||||
if (input == null) continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return.
|
if (input == null) continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return.
|
||||||
if (!input.IsConnectedTo(output)) input.Connect(output);
|
if (!input.IsConnectedTo(output)) input.Connect(output);
|
||||||
if (!_portConnectionPoints.ContainsKey(input)) continue;
|
if (!_portConnectionPoints.ContainsKey(input)) continue;
|
||||||
Vector2 to = _portConnectionPoints[input].center;
|
|
||||||
Color connectionColor = graphEditor.GetTypeColor(output.ValueType);
|
Vector2 from = _portConnectionPoints[output].center;
|
||||||
|
Vector2 to = Vector2.zero;
|
||||||
|
List<Vector2> reroutePoints = output.GetReroutePoints(k);
|
||||||
|
// Loop through reroute points and draw the path
|
||||||
|
for (int i = 0; i < reroutePoints.Count; i++) {
|
||||||
|
to = reroutePoints[i];
|
||||||
|
DrawConnection(from, to, connectionColor);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
to = _portConnectionPoints[input].center;
|
||||||
DrawConnection(from, to, connectionColor);
|
DrawConnection(from, to, connectionColor);
|
||||||
|
|
||||||
|
// Loop through reroute points again and draw the points
|
||||||
|
for (int i = 0; i < reroutePoints.Count; i++) {
|
||||||
|
RerouteReference rerouteRef = new RerouteReference(output, k, i);
|
||||||
|
// Draw reroute point at position
|
||||||
|
Rect rect = new Rect(reroutePoints[i], new Vector2(12, 12));
|
||||||
|
rect.position = new Vector2(rect.position.x - 6, rect.position.y - 6);
|
||||||
|
rect = GridToWindowRect(rect);
|
||||||
|
|
||||||
|
// Draw selected reroute points with an outline
|
||||||
|
if (selectedReroutes.Contains(rerouteRef)) {
|
||||||
|
GUI.color = NodeEditorPreferences.GetSettings().highlightColor;
|
||||||
|
GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.color = connectionColor;
|
||||||
|
GUI.DrawTexture(rect, NodeEditorResources.dot);
|
||||||
|
if (rect.Overlaps(selectionBox)) selection.Add(rerouteRef);
|
||||||
|
if (rect.Contains(mousePos)) hoveredReroute = rerouteRef;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GUI.color = col;
|
||||||
|
if (Event.current.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) selectedReroutes = selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawNodes() {
|
private void DrawNodes() {
|
||||||
@ -250,6 +304,13 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
List<UnityEngine.Object> preSelection = preBoxSelection != null ? new List<UnityEngine.Object>(preBoxSelection) : new List<UnityEngine.Object>();
|
List<UnityEngine.Object> preSelection = preBoxSelection != null ? new List<UnityEngine.Object>(preBoxSelection) : new List<UnityEngine.Object>();
|
||||||
|
|
||||||
|
// Selection box stuff
|
||||||
|
Vector2 boxStartPos = GridToWindowPositionNoClipped(dragBoxStart);
|
||||||
|
Vector2 boxSize = mousePos - boxStartPos;
|
||||||
|
if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); }
|
||||||
|
if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); }
|
||||||
|
Rect selectionBox = new Rect(boxStartPos, boxSize);
|
||||||
|
|
||||||
//Save guiColor so we can revert it
|
//Save guiColor so we can revert it
|
||||||
Color guiColor = GUI.color;
|
Color guiColor = GUI.color;
|
||||||
for (int n = 0; n < graph.nodes.Count; n++) {
|
for (int n = 0; n < graph.nodes.Count; n++) {
|
||||||
@ -319,12 +380,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
//If dragging a selection box, add nodes inside to selection
|
//If dragging a selection box, add nodes inside to selection
|
||||||
if (currentActivity == NodeActivity.DragGrid) {
|
if (currentActivity == NodeActivity.DragGrid) {
|
||||||
Vector2 startPos = GridToWindowPositionNoClipped(dragBoxStart);
|
if (windowRect.Overlaps(selectionBox)) preSelection.Add(node);
|
||||||
Vector2 size = mousePos - startPos;
|
|
||||||
if (size.x < 0) { startPos.x += size.x; size.x = Mathf.Abs(size.x); }
|
|
||||||
if (size.y < 0) { startPos.y += size.y; size.y = Mathf.Abs(size.y); }
|
|
||||||
Rect r = new Rect(startPos, size);
|
|
||||||
if (windowRect.Overlaps(r)) preSelection.Add(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if we are hovering any of this nodes ports
|
//Check if we are hovering any of this nodes ports
|
||||||
@ -332,14 +388,14 @@ namespace XNodeEditor {
|
|||||||
foreach (XNode.NodePort input in node.Inputs) {
|
foreach (XNode.NodePort input in node.Inputs) {
|
||||||
//Check if port rect is available
|
//Check if port rect is available
|
||||||
if (!portConnectionPoints.ContainsKey(input)) continue;
|
if (!portConnectionPoints.ContainsKey(input)) continue;
|
||||||
Rect r = GridToWindowRect(portConnectionPoints[input]);
|
Rect r = GridToWindowRectNoClipped(portConnectionPoints[input]);
|
||||||
if (r.Contains(mousePos)) hoveredPort = input;
|
if (r.Contains(mousePos)) hoveredPort = input;
|
||||||
}
|
}
|
||||||
//Check all output ports
|
//Check all output ports
|
||||||
foreach (XNode.NodePort output in node.Outputs) {
|
foreach (XNode.NodePort output in node.Outputs) {
|
||||||
//Check if port rect is available
|
//Check if port rect is available
|
||||||
if (!portConnectionPoints.ContainsKey(output)) continue;
|
if (!portConnectionPoints.ContainsKey(output)) continue;
|
||||||
Rect r = GridToWindowRect(portConnectionPoints[output]);
|
Rect r = GridToWindowRectNoClipped(portConnectionPoints[output]);
|
||||||
if (r.Contains(mousePos)) hoveredPort = output;
|
if (r.Contains(mousePos)) hoveredPort = output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,7 +128,7 @@ namespace XNodeEditor {
|
|||||||
else NodeEditor.portPositions.Add(port, portPos);
|
else NodeEditor.portPositions.Add(port, portPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) {
|
public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) {
|
||||||
Color col = GUI.color;
|
Color col = GUI.color;
|
||||||
GUI.color = backgroundColor;
|
GUI.color = backgroundColor;
|
||||||
GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
|
GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
public Color32 highlightColor = new Color32(255, 255, 255, 255);
|
public Color32 highlightColor = new Color32(255, 255, 255, 255);
|
||||||
public bool gridSnap = true;
|
public bool gridSnap = true;
|
||||||
|
public bool autoSave = true;
|
||||||
[SerializeField] private string typeColorsData = "";
|
[SerializeField] private string typeColorsData = "";
|
||||||
[NonSerialized] public Dictionary<string, Color> typeColors = new Dictionary<string, Color>();
|
[NonSerialized] public Dictionary<string, Color> typeColors = new Dictionary<string, Color>();
|
||||||
public NoodleType noodleType = NoodleType.Curve;
|
public NoodleType noodleType = NoodleType.Curve;
|
||||||
@ -73,9 +74,9 @@ namespace XNodeEditor {
|
|||||||
XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute attrib = attribs[0] as XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute;
|
XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute attrib = attribs[0] as XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute;
|
||||||
lastEditor = XNodeEditor.NodeEditorWindow.current.graphEditor;
|
lastEditor = XNodeEditor.NodeEditorWindow.current.graphEditor;
|
||||||
lastKey = attrib.editorPrefsKey;
|
lastKey = attrib.editorPrefsKey;
|
||||||
VerifyLoaded();
|
|
||||||
} else return null;
|
} else return null;
|
||||||
}
|
}
|
||||||
|
if (!settings.ContainsKey(lastKey)) VerifyLoaded();
|
||||||
return settings[lastKey];
|
return settings[lastKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +87,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
NodeSettingsGUI(lastKey, settings);
|
NodeSettingsGUI(lastKey, settings);
|
||||||
GridSettingsGUI(lastKey, settings);
|
GridSettingsGUI(lastKey, settings);
|
||||||
|
SystemSettingsGUI(lastKey, settings);
|
||||||
TypeColorsGUI(lastKey, settings);
|
TypeColorsGUI(lastKey, settings);
|
||||||
if (GUILayout.Button(new GUIContent("Set Default", "Reset all values to default"), GUILayout.Width(120))) {
|
if (GUILayout.Button(new GUIContent("Set Default", "Reset all values to default"), GUILayout.Width(120))) {
|
||||||
ResetPrefs();
|
ResetPrefs();
|
||||||
@ -95,7 +97,7 @@ namespace XNodeEditor {
|
|||||||
private static void GridSettingsGUI(string key, Settings settings) {
|
private static void GridSettingsGUI(string key, Settings settings) {
|
||||||
//Label
|
//Label
|
||||||
EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel);
|
EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel);
|
||||||
settings.gridSnap = EditorGUILayout.Toggle("Snap", settings.gridSnap);
|
settings.gridSnap = EditorGUILayout.Toggle(new GUIContent("Snap", "Hold CTRL in editor to invert"), settings.gridSnap);
|
||||||
|
|
||||||
settings.gridLineColor = EditorGUILayout.ColorField("Color", settings.gridLineColor);
|
settings.gridLineColor = EditorGUILayout.ColorField("Color", settings.gridLineColor);
|
||||||
settings.gridBgColor = EditorGUILayout.ColorField(" ", settings.gridBgColor);
|
settings.gridBgColor = EditorGUILayout.ColorField(" ", settings.gridBgColor);
|
||||||
@ -107,6 +109,14 @@ namespace XNodeEditor {
|
|||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SystemSettingsGUI(string key, Settings settings) {
|
||||||
|
//Label
|
||||||
|
EditorGUILayout.LabelField("System", EditorStyles.boldLabel);
|
||||||
|
settings.autoSave = EditorGUILayout.Toggle(new GUIContent("Autosave", "Disable for better editor performance"), settings.autoSave);
|
||||||
|
if (GUI.changed) SavePrefs(key, settings);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
}
|
||||||
|
|
||||||
private static void NodeSettingsGUI(string key, Settings settings) {
|
private static void NodeSettingsGUI(string key, Settings settings) {
|
||||||
//Label
|
//Label
|
||||||
EditorGUILayout.LabelField("Node", EditorStyles.boldLabel);
|
EditorGUILayout.LabelField("Node", EditorStyles.boldLabel);
|
||||||
|
|||||||
@ -11,10 +11,12 @@ namespace XNodeEditor {
|
|||||||
public partial class NodeEditorWindow {
|
public partial class NodeEditorWindow {
|
||||||
/// <summary> Custom node tint colors defined with [NodeColor(r, g, b)] </summary>
|
/// <summary> Custom node tint colors defined with [NodeColor(r, g, b)] </summary>
|
||||||
public static Dictionary<Type, Color> nodeTint { get { return _nodeTint != null ? _nodeTint : _nodeTint = GetNodeTint(); } }
|
public static Dictionary<Type, Color> nodeTint { get { return _nodeTint != null ? _nodeTint : _nodeTint = GetNodeTint(); } }
|
||||||
[NonSerialized] private static Dictionary<Type, Color> _nodeTint;
|
|
||||||
|
[NonSerialized] private static Dictionary<Type, Color> _nodeTint;
|
||||||
/// <summary> All available node types </summary>
|
/// <summary> All available node types </summary>
|
||||||
public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
|
public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
|
||||||
[NonSerialized] private static Type[] _nodeTypes = null;
|
|
||||||
|
[NonSerialized] private static Type[] _nodeTypes = null;
|
||||||
|
|
||||||
public static Type[] GetNodeTypes() {
|
public static Type[] GetNodeTypes() {
|
||||||
//Get all classes deriving from Node via reflection
|
//Get all classes deriving from Node via reflection
|
||||||
@ -32,13 +34,14 @@ namespace XNodeEditor {
|
|||||||
return tints;
|
return tints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Get all classes deriving from baseType via reflection </summary>
|
||||||
public static Type[] GetDerivedTypes(Type baseType) {
|
public static Type[] GetDerivedTypes(Type baseType) {
|
||||||
//Get all classes deriving from baseType via reflection
|
List<System.Type> types = new List<System.Type>();
|
||||||
Assembly assembly = Assembly.GetAssembly(baseType);
|
System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||||
return assembly.GetTypes().Where(t =>
|
foreach (Assembly assembly in assemblies) {
|
||||||
!t.IsAbstract &&
|
types.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
|
||||||
baseType.IsAssignableFrom(t)
|
}
|
||||||
).ToArray();
|
return types.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object ObjectFromType(Type type) {
|
public static object ObjectFromType(Type type) {
|
||||||
@ -71,10 +74,10 @@ namespace XNodeEditor {
|
|||||||
kvp.Add(new KeyValuePair<ContextMenu, MethodInfo>(attribs[k], methods[i]));
|
kvp.Add(new KeyValuePair<ContextMenu, MethodInfo>(attribs[k], methods[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if UNITY_5_5_OR_NEWER
|
#if UNITY_5_5_OR_NEWER
|
||||||
//Sort menu items
|
//Sort menu items
|
||||||
kvp.Sort((x, y) => x.Key.priority.CompareTo(y.Key.priority));
|
kvp.Sort((x, y) => x.Key.priority.CompareTo(y.Key.priority));
|
||||||
#endif
|
#endif
|
||||||
return kvp.ToArray();
|
return kvp.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,9 @@ namespace XNodeEditor {
|
|||||||
private float _zoom = 1;
|
private float _zoom = 1;
|
||||||
|
|
||||||
void OnFocus() {
|
void OnFocus() {
|
||||||
AssetDatabase.SaveAssets();
|
|
||||||
current = this;
|
current = this;
|
||||||
|
graphEditor = NodeGraphEditor.GetEditor(graph);
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnEnable();
|
partial void OnEnable();
|
||||||
@ -37,7 +38,7 @@ namespace XNodeEditor {
|
|||||||
public void Save() {
|
public void Save() {
|
||||||
if (AssetDatabase.Contains(graph)) {
|
if (AssetDatabase.Contains(graph)) {
|
||||||
EditorUtility.SetDirty(graph);
|
EditorUtility.SetDirty(graph);
|
||||||
AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
} else SaveAs();
|
} else SaveAs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ namespace XNodeEditor {
|
|||||||
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();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,11 +66,17 @@ namespace XNodeEditor {
|
|||||||
return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition / zoom);
|
return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition / zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rect GridToWindowRect(Rect gridRect) {
|
public Rect GridToWindowRectNoClipped(Rect gridRect) {
|
||||||
gridRect.position = GridToWindowPositionNoClipped(gridRect.position);
|
gridRect.position = GridToWindowPositionNoClipped(gridRect.position);
|
||||||
return gridRect;
|
return gridRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|||||||
@ -8,8 +8,12 @@ namespace XNodeEditor {
|
|||||||
/// <summary> Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. </summary>
|
/// <summary> Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. </summary>
|
||||||
[CustomNodeGraphEditor(typeof(XNode.NodeGraph))]
|
[CustomNodeGraphEditor(typeof(XNode.NodeGraph))]
|
||||||
public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase<NodeGraphEditor, NodeGraphEditor.CustomNodeGraphEditorAttribute, XNode.NodeGraph> {
|
public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase<NodeGraphEditor, NodeGraphEditor.CustomNodeGraphEditorAttribute, XNode.NodeGraph> {
|
||||||
/// <summary> Custom node editors defined with [CustomNodeGraphEditor] </summary>
|
/// <summary> The position of the window in screen space. </summary>
|
||||||
[NonSerialized] private static Dictionary<Type, NodeGraphEditor> editors;
|
public Rect position;
|
||||||
|
/// <summary> Are we currently renaming a node? </summary>
|
||||||
|
protected bool isRenaming;
|
||||||
|
|
||||||
|
public virtual void OnGUI() { }
|
||||||
|
|
||||||
public virtual Texture2D GetGridTexture() {
|
public virtual Texture2D GetGridTexture() {
|
||||||
return NodeEditorPreferences.GetSettings().gridTexture;
|
return NodeEditorPreferences.GetSettings().gridTexture;
|
||||||
@ -38,6 +42,22 @@ namespace XNodeEditor {
|
|||||||
return NodeEditorPreferences.GetTypeColor(type);
|
return NodeEditorPreferences.GetTypeColor(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Creates a copy of the original node in the graph </summary>
|
||||||
|
public XNode.Node CopyNode(XNode.Node original) {
|
||||||
|
XNode.Node node = target.CopyNode(original);
|
||||||
|
node.name = original.name;
|
||||||
|
AssetDatabase.AddObjectToAsset(node, target);
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Safely remove a node and all its connections. </summary>
|
||||||
|
public void RemoveNode(XNode.Node node) {
|
||||||
|
UnityEngine.Object.DestroyImmediate(node, true);
|
||||||
|
target.RemoveNode(node);
|
||||||
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class CustomNodeGraphEditorAttribute : Attribute,
|
public class CustomNodeGraphEditorAttribute : Attribute,
|
||||||
XNodeEditor.Internal.NodeEditorBase<NodeGraphEditor, NodeGraphEditor.CustomNodeGraphEditorAttribute, XNode.NodeGraph>.INodeEditorAttrib {
|
XNodeEditor.Internal.NodeEditorBase<NodeGraphEditor, NodeGraphEditor.CustomNodeGraphEditorAttribute, XNode.NodeGraph>.INodeEditorAttrib {
|
||||||
|
|||||||
@ -71,7 +71,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Initialize node. Called on creation. </summary>
|
/// <summary> Initialize node. Called on creation. </summary>
|
||||||
protected virtual void Init() { name = GetType().Name; }
|
protected virtual void Init() { }
|
||||||
|
|
||||||
/// <summary> Checks all connections for invalid references, and removes them. </summary>
|
/// <summary> Checks all connections for invalid references, and removes them. </summary>
|
||||||
public void VerifyConnections() {
|
public void VerifyConnections() {
|
||||||
@ -127,7 +127,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Removes all instance ports from the node </summary>
|
/// <summary> Removes all instance ports from the node </summary>
|
||||||
[ContextMenu("Clear instance ports")]
|
[ContextMenu("Clear Instance Ports")]
|
||||||
public void ClearInstancePorts() {
|
public void ClearInstancePorts() {
|
||||||
List<NodePort> instancePorts = new List<NodePort>(InstancePorts);
|
List<NodePort> instancePorts = new List<NodePort>(InstancePorts);
|
||||||
foreach (NodePort port in instancePorts) {
|
foreach (NodePort port in instancePorts) {
|
||||||
|
|||||||
@ -46,13 +46,19 @@ namespace XNode {
|
|||||||
private static void BuildCache() {
|
private static void BuildCache() {
|
||||||
portDataCache = new PortDataCache();
|
portDataCache = new PortDataCache();
|
||||||
System.Type baseType = typeof(Node);
|
System.Type baseType = typeof(Node);
|
||||||
Assembly assembly = Assembly.GetAssembly(baseType);
|
List<System.Type> nodeTypes = new List<System.Type>();
|
||||||
System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
|
System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||||
!t.IsAbstract &&
|
Assembly selfAssembly = Assembly.GetAssembly(baseType);
|
||||||
baseType.IsAssignableFrom(t)
|
if (selfAssembly.FullName.StartsWith("Assembly-CSharp")) {
|
||||||
).ToArray();
|
// If xNode is not used as a DLL, check only CSharp (fast)
|
||||||
|
nodeTypes.AddRange(selfAssembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)));
|
||||||
for (int i = 0; i < nodeTypes.Length; i++) {
|
} else {
|
||||||
|
// Else, check all DDLs (slow)
|
||||||
|
foreach (Assembly assembly in assemblies) {
|
||||||
|
nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nodeTypes.Count; i++) {
|
||||||
CachePorts(nodeTypes[i]);
|
CachePorts(nodeTypes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,4 +109,4 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,13 +19,6 @@ namespace XNode {
|
|||||||
/// <summary> Add a node to the graph by type </summary>
|
/// <summary> Add a node to the graph by type </summary>
|
||||||
public virtual Node AddNode(Type type) {
|
public virtual Node AddNode(Type type) {
|
||||||
Node node = ScriptableObject.CreateInstance(type) as Node;
|
Node node = ScriptableObject.CreateInstance(type) as Node;
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!Application.isPlaying) {
|
|
||||||
UnityEditor.AssetDatabase.AddObjectToAsset(node, this);
|
|
||||||
UnityEditor.AssetDatabase.SaveAssets();
|
|
||||||
node.name = UnityEditor.ObjectNames.NicifyVariableName(node.name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
nodes.Add(node);
|
nodes.Add(node);
|
||||||
node.graph = this;
|
node.graph = this;
|
||||||
return node;
|
return node;
|
||||||
@ -35,13 +28,6 @@ namespace XNode {
|
|||||||
public virtual Node CopyNode(Node original) {
|
public virtual Node CopyNode(Node original) {
|
||||||
Node node = ScriptableObject.Instantiate(original);
|
Node node = ScriptableObject.Instantiate(original);
|
||||||
node.ClearConnections();
|
node.ClearConnections();
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!Application.isPlaying) {
|
|
||||||
UnityEditor.AssetDatabase.AddObjectToAsset(node, this);
|
|
||||||
UnityEditor.AssetDatabase.SaveAssets();
|
|
||||||
node.name = UnityEditor.ObjectNames.NicifyVariableName(node.name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
nodes.Add(node);
|
nodes.Add(node);
|
||||||
node.graph = this;
|
node.graph = this;
|
||||||
return node;
|
return node;
|
||||||
@ -51,12 +37,6 @@ namespace XNode {
|
|||||||
/// <param name="node"></param>
|
/// <param name="node"></param>
|
||||||
public void RemoveNode(Node node) {
|
public void RemoveNode(Node node) {
|
||||||
node.ClearConnections();
|
node.ClearConnections();
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!Application.isPlaying) {
|
|
||||||
DestroyImmediate(node, true);
|
|
||||||
UnityEditor.AssetDatabase.SaveAssets();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
nodes.Remove(node);
|
nodes.Remove(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +51,7 @@ namespace XNode {
|
|||||||
NodeGraph graph = Instantiate(this);
|
NodeGraph graph = Instantiate(this);
|
||||||
// Instantiate all nodes inside the graph
|
// Instantiate all nodes inside the graph
|
||||||
for (int i = 0; i < nodes.Count; i++) {
|
for (int i = 0; i < nodes.Count; i++) {
|
||||||
|
if (nodes[i] == null) continue;
|
||||||
Node node = Instantiate(nodes[i]) as Node;
|
Node node = Instantiate(nodes[i]) as Node;
|
||||||
node.graph = graph;
|
node.graph = graph;
|
||||||
graph.nodes[i] = node;
|
graph.nodes[i] = node;
|
||||||
@ -78,6 +59,7 @@ namespace XNode {
|
|||||||
|
|
||||||
// Redirect all connections
|
// Redirect all connections
|
||||||
for (int i = 0; i < graph.nodes.Count; i++) {
|
for (int i = 0; i < graph.nodes.Count; i++) {
|
||||||
|
if (graph.nodes[i] == null) continue;
|
||||||
foreach (NodePort port in graph.nodes[i].Ports) {
|
foreach (NodePort port in graph.nodes[i].Ports) {
|
||||||
port.Redirect(nodes, graph.nodes);
|
port.Redirect(nodes, graph.nodes);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -216,6 +216,14 @@ namespace XNode {
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Get index of the connection connecting this and specified ports </summary>
|
||||||
|
public int GetConnectionIndex(NodePort port) {
|
||||||
|
for (int i = 0; i < ConnectionCount; i++) {
|
||||||
|
if (connections[i].Port == port) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsConnectedTo(NodePort port) {
|
public bool IsConnectedTo(NodePort port) {
|
||||||
for (int i = 0; i < connections.Count; i++) {
|
for (int i = 0; i < connections.Count; i++) {
|
||||||
if (connections[i].Port == port) return true;
|
if (connections[i].Port == port) return true;
|
||||||
@ -250,6 +258,11 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Get reroute points for a given connection. This is used for organization </summary>
|
||||||
|
public List<Vector2> GetReroutePoints(int index) {
|
||||||
|
return connections[index].reroutePoints;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Swap connected nodes from the old list with nodes from the new list </summary>
|
/// <summary> Swap connected nodes from the old list with nodes from the new list </summary>
|
||||||
public void Redirect(List<Node> oldNodes, List<Node> newNodes) {
|
public void Redirect(List<Node> oldNodes, List<Node> newNodes) {
|
||||||
foreach (PortConnection connection in connections) {
|
foreach (PortConnection connection in connections) {
|
||||||
@ -265,6 +278,8 @@ namespace XNode {
|
|||||||
public NodePort Port { get { return port != null ? port : port = GetPort(); } }
|
public NodePort Port { get { return port != null ? port : port = GetPort(); } }
|
||||||
|
|
||||||
[NonSerialized] private NodePort port;
|
[NonSerialized] private NodePort port;
|
||||||
|
/// <summary> Extra connection path points for organization </summary>
|
||||||
|
[SerializeField] public List<Vector2> reroutePoints = new List<Vector2>();
|
||||||
|
|
||||||
public PortConnection(NodePort port) {
|
public PortConnection(NodePort port) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user