diff --git a/Examples/MathGraph/Editor/MathGraphEditor.cs b/Examples/MathGraph/Editor/MathGraphEditor.cs
index 06cd431..7a580bf 100644
--- a/Examples/MathGraph/Editor/MathGraphEditor.cs
+++ b/Examples/MathGraph/Editor/MathGraphEditor.cs
@@ -8,12 +8,12 @@ namespace XNodeEditor.Examples {
public class MathGraphEditor : NodeGraphEditor {
///
- /// Overriding GetNodePath lets you control if and how nodes are categorized.
+ /// Overriding GetNodeMenuName lets you control if and how nodes are categorized.
/// In this example we are sorting out all node types that are not in the XNode.Examples namespace.
///
- public override string GetNodePath(System.Type type) {
+ public override string GetNodeMenuName(System.Type type) {
if (type.Namespace == "XNode.Examples.MathNodes") {
- return base.GetNodePath(type).Replace("X Node/Examples/Math Nodes/", "");
+ return base.GetNodeMenuName(type).Replace("X Node/Examples/Math Nodes/", "");
} else return null;
}
}
diff --git a/Examples/StateMachine/Editor/StateGraphEditor.cs b/Examples/StateMachine/Editor/StateGraphEditor.cs
index f1b9cab..cbc810a 100644
--- a/Examples/StateMachine/Editor/StateGraphEditor.cs
+++ b/Examples/StateMachine/Editor/StateGraphEditor.cs
@@ -9,12 +9,12 @@ namespace XNodeEditor.Examples {
public class StateGraphEditor : NodeGraphEditor {
///
- /// Overriding GetNodePath lets you control if and how nodes are categorized.
+ /// Overriding GetNodeMenuName lets you control if and how nodes are categorized.
/// In this example we are sorting out all node types that are not in the XNode.Examples namespace.
///
- public override string GetNodePath(System.Type type) {
+ public override string GetNodeMenuName(System.Type type) {
if (type.Namespace == "XNode.Examples.StateGraph") {
- return base.GetNodePath(type).Replace("X Node/Examples/State Graph/", "");
+ return base.GetNodeMenuName(type).Replace("X Node/Examples/State Graph/", "");
} else return null;
}
}
diff --git a/README.md b/README.md
index e0544a7..dcaa133 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
+
[](https://discord.gg/qgPrHv4)
[](https://github.com/Siccity/xNode/issues)
[](https://raw.githubusercontent.com/Siccity/xNode/master/LICENSE.md)
@@ -5,6 +7,8 @@
[Downloads](https://github.com/Siccity/xNode/releases) / [Asset Store](http://u3d.as/108S) / [Documentation](https://github.com/Siccity/xNode/wiki)
+[Support Me on Ko-fi](https://ko-fi.com/Z8Z5DYWA)
+
### xNode
Thinking of developing a node-based plugin? Then this is for you. You can download it as an archive and unpack to a new unity project, or connect it as git submodule.
diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs
index 8a64663..475a3b6 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/Scripts/Editor/NodeEditor.cs
@@ -23,7 +23,6 @@ namespace XNodeEditor {
}
public virtual void OnHeaderGUI() {
- GUI.color = Color.white;
string title = target.name;
if (renaming != 0 && Selection.Contains(target)) {
int controlID = EditorGUIUtility.GetControlID(FocusType.Keyboard) + 1;
@@ -58,7 +57,9 @@ namespace XNodeEditor {
}
public virtual int GetWidth() {
- return 208;
+ Type type = target.GetType();
+ if (NodeEditorWindow.nodeWidth.ContainsKey(type)) return NodeEditorWindow.nodeWidth[type];
+ else return 208;
}
public virtual Color GetTint() {
diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs
index 651824a..2996938 100644
--- a/Scripts/Editor/NodeEditorAction.cs
+++ b/Scripts/Editor/NodeEditorAction.cs
@@ -203,16 +203,13 @@ namespace XNodeEditor {
EditorUtility.SetDirty(graph);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} else if (currentActivity == NodeActivity.DragNode) {
+ IEnumerable nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node);
+ foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} else if (!IsHoveringNode) {
// If click outside node, release field focus
if (!isPanning) {
- // I've got no idea which of these do what, so we'll just reset all of it.
- GUIUtility.hotControl = 0;
- GUIUtility.keyboardControl = 0;
- EditorGUIUtility.editingTextField = false;
- EditorGUIUtility.keyboardControl = 0;
- EditorGUIUtility.hotControl = 0;
+ EditorGUI.FocusTextInControl(null);
}
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
}
@@ -231,7 +228,7 @@ namespace XNodeEditor {
Repaint();
currentActivity = NodeActivity.Idle;
- } else if (e.button == 1) {
+ } else if (e.button == 1 || e.button == 2) {
if (!isPanning) {
if (IsDraggingPort) {
draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
@@ -255,6 +252,11 @@ namespace XNodeEditor {
case EventType.KeyDown:
if (EditorGUIUtility.editingTextField) break;
else if (e.keyCode == KeyCode.F) Home();
+ if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) {
+ if (e.keyCode == KeyCode.Return) RenameSelectedNode();
+ } else {
+ if (e.keyCode == KeyCode.F2) RenameSelectedNode();
+ }
break;
case EventType.ValidateCommand:
if (e.commandName == "SoftDelete") RemoveSelectedNodes();
@@ -296,7 +298,7 @@ namespace XNodeEditor {
public void CreateNode(Type type, Vector2 position) {
XNode.Node node = graph.AddNode(type);
node.position = position;
- node.name = UnityEditor.ObjectNames.NicifyVariableName(type.ToString());
+ node.name = UnityEditor.ObjectNames.NicifyVariableName(type.Name);
AssetDatabase.AddObjectToAsset(node, graph);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
Repaint();
@@ -318,6 +320,14 @@ namespace XNodeEditor {
}
}
+ /// Initiate a rename on the currently selected node
+ public void RenameSelectedNode() {
+ if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
+ XNode.Node node = Selection.activeObject as XNode.Node;
+ NodeEditor.GetEditor(node).InitiateRename();
+ }
+ }
+
/// Draw this node on top of other nodes by placing it last in the graph.nodes list
public void MoveNodeToTop(XNode.Node node) {
int index;
@@ -407,7 +417,7 @@ namespace XNodeEditor {
//Get node position
Vector2 nodePos = GridToWindowPosition(node.position);
float width = 200;
- if (nodeWidths.ContainsKey(node)) width = nodeWidths[node];
+ if (nodeSizes.ContainsKey(node)) width = nodeSizes[node].x;
Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
return windowRect.Contains(mousePos);
}
diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs
index 34cd210..cda5b98 100644
--- a/Scripts/Editor/NodeEditorGUI.cs
+++ b/Scripts/Editor/NodeEditorGUI.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using UnityEditor;
using UnityEngine;
@@ -8,6 +9,7 @@ namespace XNodeEditor {
public partial class NodeEditorWindow {
public NodeGraphEditor graphEditor;
private List selectionCache;
+ private List culledNodes;
private void OnGUI() {
Event e = Event.current;
@@ -112,7 +114,7 @@ namespace XNodeEditor {
if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
XNode.Node node = Selection.activeObject as XNode.Node;
contextMenu.AddItem(new GUIContent("Move To Top"), false, () => MoveNodeToTop(node));
- contextMenu.AddItem(new GUIContent("Rename"), false, NodeEditor.GetEditor(node).InitiateRename);
+ contextMenu.AddItem(new GUIContent("Rename"), false, RenameSelectedNode);
}
contextMenu.AddItem(new GUIContent("Duplicate"), false, DublicateSelectedNodes);
@@ -135,8 +137,8 @@ namespace XNodeEditor {
Type type = nodeTypes[i];
//Get node context menu path
- string path = graphEditor.GetNodePath(type);
- if (path == null) continue;
+ string path = graphEditor.GetNodeMenuName(type);
+ if (string.IsNullOrEmpty(path)) continue;
contextMenu.AddItem(new GUIContent(path), false, () => {
CreateNode(type, pos);
@@ -280,10 +282,6 @@ namespace XNodeEditor {
if (e.type == EventType.Layout) {
selectionCache = new List(Selection.objects);
}
- if (e.type == EventType.Repaint) {
- portConnectionPoints.Clear();
- nodeWidths.Clear();
- }
//Active node is hashed before and after node GUI to detect changes
int nodeHash = 0;
@@ -313,6 +311,8 @@ namespace XNodeEditor {
//Save guiColor so we can revert it
Color guiColor = GUI.color;
+
+ if (e.type == EventType.Layout) culledNodes = new List();
for (int n = 0; n < graph.nodes.Count; n++) {
// Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable.
if (graph.nodes[n] == null) continue;
@@ -320,6 +320,20 @@ namespace XNodeEditor {
XNode.Node node = graph.nodes[n];
NodeEditor nodeEditor = NodeEditor.GetEditor(node);
+
+ // Culling
+ if (e.type == EventType.Layout) {
+ // Cull unselected nodes outside view
+ if (!Selection.Contains(node) && ShouldBeCulled(nodeEditor)) {
+ culledNodes.Add(node);
+ continue;
+ }
+ } else if (culledNodes.Contains(node)) continue;
+
+ if (e.type == EventType.Repaint) {
+ _portConnectionPoints = _portConnectionPoints.Where(x => x.Key.node != node).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+ }
+
NodeEditor.portPositions = new Dictionary();
//Get node position
@@ -350,26 +364,30 @@ namespace XNodeEditor {
//Draw node contents
nodeEditor.OnNodeGUI();
- //Apply
- nodeEditor.serializedObject.ApplyModifiedProperties();
-
//If user changed a value, notify other scripts through onUpdateNode
if (EditorGUI.EndChangeCheck()) {
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
+ EditorUtility.SetDirty(node);
+ nodeEditor.serializedObject.ApplyModifiedProperties();
}
+ GUILayout.EndVertical();
+
+ //Cache data about the node for next frame
if (e.type == EventType.Repaint) {
- nodeWidths.Add(node, nodeEditor.GetWidth());
+ Vector2 size = GUILayoutUtility.GetLastRect().size;
+ if (nodeSizes.ContainsKey(node)) nodeSizes[node] = size;
+ else nodeSizes.Add(node, size);
foreach (var kvp in NodeEditor.portPositions) {
Vector2 portHandlePos = kvp.Value;
portHandlePos += node.position;
Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);
- portConnectionPoints.Add(kvp.Key, rect);
+ if (portConnectionPoints.ContainsKey(kvp.Key)) portConnectionPoints[kvp.Key] = rect;
+ else portConnectionPoints.Add(kvp.Key, rect);
}
}
- GUILayout.EndVertical();
if (selected) GUILayout.EndVertical();
if (e.type != EventType.Layout) {
@@ -414,12 +432,25 @@ namespace XNodeEditor {
}
}
+ /// Returns true if outside window area
+ private bool ShouldBeCulled(XNodeEditor.NodeEditor nodeEditor) {
+ Vector2 nodePos = GridToWindowPositionNoClipped(nodeEditor.target.position);
+ if (nodePos.x / _zoom > position.width) return true; // Right
+ else if (nodePos.y / _zoom > position.height) return true; // Bottom
+ else if (nodeSizes.ContainsKey(nodeEditor.target)) {
+ Vector2 size = nodeSizes[nodeEditor.target];
+ if (nodePos.x + size.x < 0) return true; // Left
+ else if (nodePos.y + size.y < 0) return true; // Top
+ }
+ return false;
+ }
+
private void DrawTooltip() {
if (hoveredPort != null) {
Type type = hoveredPort.ValueType;
GUIContent content = new GUIContent();
content.text = type.PrettyName();
- if (hoveredPort.IsStatic && hoveredPort.IsOutput) {
+ if (hoveredPort.IsOutput) {
object obj = hoveredPort.node.GetValue(hoveredPort);
content.text += " = " + (obj != null ? obj.ToString() : "null");
}
diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs
index 8a629e8..cfd3c67 100644
--- a/Scripts/Editor/NodeEditorGUILayout.cs
+++ b/Scripts/Editor/NodeEditorGUILayout.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
@@ -110,11 +112,26 @@ namespace XNodeEditor {
/// Make a simple port field.
public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) {
if (port == null) return;
- if (label == null) EditorGUILayout.LabelField(ObjectNames.NicifyVariableName(port.fieldName), options);
- else EditorGUILayout.LabelField(label, options);
- Rect rect = GUILayoutUtility.GetLastRect();
- if (port.direction == XNode.NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0);
- else if (port.direction == XNode.NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0);
+ if (options == null) options = new GUILayoutOption[] { GUILayout.MinWidth(30) };
+ Rect rect = new Rect();
+ GUIContent content = label != null ? label : new GUIContent(ObjectNames.NicifyVariableName(port.fieldName));
+
+ // If property is an input, display a regular property field and put a port handle on the left side
+ if (port.direction == XNode.NodePort.IO.Input) {
+ // Display a label
+ EditorGUILayout.LabelField(content, options);
+
+ rect = GUILayoutUtility.GetLastRect();
+ rect.position = rect.position - new Vector2(16, 0);
+ // If property is an output, display a text label and put a port handle on the right side
+ } else if (port.direction == XNode.NodePort.IO.Output) {
+ // Display a label
+ EditorGUILayout.LabelField(content, NodeEditorResources.OutputPort, options);
+
+ rect = GUILayoutUtility.GetLastRect();
+ rect.position = rect.position + new Vector2(rect.width, 0);
+ }
+
rect.size = new Vector2(16, 16);
Color backgroundColor = new Color32(90, 97, 105, 255);
@@ -128,13 +145,87 @@ namespace XNodeEditor {
else NodeEditor.portPositions.Add(port, portPos);
}
+ /// Draws an input and an output port on the same line
+ public static void PortPair(XNode.NodePort input, XNode.NodePort output) {
+ GUILayout.BeginHorizontal();
+ NodeEditorGUILayout.PortField(input, GUILayout.MinWidth(0));
+ NodeEditorGUILayout.PortField(output, GUILayout.MinWidth(0));
+ GUILayout.EndHorizontal();
+ }
+
public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) {
Color col = GUI.color;
GUI.color = backgroundColor;
GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
- GUI.color = typeColor;
+ GUI.color = typeColor;
GUI.DrawTexture(rect, NodeEditorResources.dot);
GUI.color = col;
}
+
+ /// Draw an editable list of instance ports. Port names are named as "[fieldName] [index]"
+ /// Supply a list for editable values
+ /// Value type of added instance ports
+ /// The serializedObject of the node
+ /// Connection type of added instance ports
+ public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple) {
+ XNode.Node node = serializedObject.targetObject as XNode.Node;
+ SerializedProperty arrayData = serializedObject.FindProperty(fieldName);
+ bool hasArrayData = arrayData != null && arrayData.isArray;
+ int arraySize = hasArrayData ? arrayData.arraySize : 0;
+
+ List instancePorts = node.InstancePorts.Where(x => x.fieldName.StartsWith(fieldName)).OrderBy(x => x.fieldName).ToList();
+
+ for (int i = 0; i < instancePorts.Count(); i++) {
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("-", GUILayout.Width(30))) {
+ // Clear the removed ports connections
+ instancePorts[i].ClearConnections();
+ // Move following connections one step up to replace the missing connection
+ for (int k = i + 1; k < instancePorts.Count(); k++) {
+ for (int j = 0; j < instancePorts[k].ConnectionCount; j++) {
+ XNode.NodePort other = instancePorts[k].GetConnection(j);
+ instancePorts[k].Disconnect(other);
+ instancePorts[k - 1].Connect(other);
+ }
+ }
+ // Remove the last instance port, to avoid messing up the indexing
+ node.RemoveInstancePort(instancePorts[instancePorts.Count() - 1].fieldName);
+ serializedObject.Update();
+ EditorUtility.SetDirty(node);
+ if (hasArrayData) {
+ arrayData.DeleteArrayElementAtIndex(i);
+ arraySize--;
+ }
+ i--;
+ } else {
+ if (hasArrayData) {
+ if (i < arraySize) {
+ SerializedProperty itemData = arrayData.GetArrayElementAtIndex(i);
+ if (itemData != null) EditorGUILayout.PropertyField(itemData, new GUIContent(ObjectNames.NicifyVariableName(fieldName) + " " + i));
+ else EditorGUILayout.LabelField("[Missing array data]");
+ } else EditorGUILayout.LabelField("[Out of bounds]");
+
+ } else {
+ EditorGUILayout.LabelField(instancePorts[i].fieldName);
+ }
+ NodeEditorGUILayout.PortField(new GUIContent(), node.GetPort(instancePorts[i].fieldName), GUILayout.Width(-4));
+ }
+ GUILayout.EndHorizontal();
+ }
+ GUILayout.BeginHorizontal();
+ EditorGUILayout.Space();
+ if (GUILayout.Button("+", GUILayout.Width(30))) {
+
+ string newName = fieldName + " 0";
+ int i = 0;
+ while (node.HasPort(newName)) newName = fieldName + " " + (++i);
+
+ instancePorts.Add(node.AddInstanceOutput(type, connectionType, newName));
+ serializedObject.Update();
+ EditorUtility.SetDirty(node);
+ if (hasArrayData) arrayData.InsertArrayElementAtIndex(arraySize);
+ }
+ GUILayout.EndHorizontal();
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs
index 6cdbc42..7eaec2b 100644
--- a/Scripts/Editor/NodeEditorReflection.cs
+++ b/Scripts/Editor/NodeEditorReflection.cs
@@ -13,6 +13,10 @@ namespace XNodeEditor {
public static Dictionary nodeTint { get { return _nodeTint != null ? _nodeTint : _nodeTint = GetNodeTint(); } }
[NonSerialized] private static Dictionary _nodeTint;
+ /// Custom node widths defined with [NodeWidth(width)]
+ public static Dictionary nodeWidth { get { return _nodeWidth != null ? _nodeWidth : _nodeWidth = GetNodeWidth(); } }
+
+ [NonSerialized] private static Dictionary _nodeWidth;
/// All available node types
public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
@@ -34,6 +38,17 @@ namespace XNodeEditor {
return tints;
}
+ public static Dictionary GetNodeWidth() {
+ Dictionary widths = new Dictionary();
+ for (int i = 0; i < nodeTypes.Length; i++) {
+ var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.Node.NodeWidth), true);
+ if (attribs == null || attribs.Length == 0) continue;
+ XNode.Node.NodeWidth attrib = attribs[0] as XNode.Node.NodeWidth;
+ widths.Add(nodeTypes[i], attrib.width);
+ }
+ return widths;
+ }
+
/// Get all classes deriving from baseType via reflection
public static Type[] GetDerivedTypes(Type baseType) {
List types = new List();
diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs
index db16033..7fdc49d 100644
--- a/Scripts/Editor/NodeEditorWindow.cs
+++ b/Scripts/Editor/NodeEditorWindow.cs
@@ -11,8 +11,8 @@ namespace XNodeEditor {
/// Stores node positions for all nodePorts.
public Dictionary portConnectionPoints { get { return _portConnectionPoints; } }
private Dictionary _portConnectionPoints = new Dictionary();
- public Dictionary nodeWidths { get { return _nodeWidths; } }
- private Dictionary _nodeWidths = new Dictionary();
+ public Dictionary nodeSizes { get { return _nodeSizes; } }
+ private Dictionary _nodeSizes = new Dictionary();
public XNode.NodeGraph graph;
public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } }
private Vector2 _panOffset;
@@ -22,7 +22,7 @@ namespace XNodeEditor {
void OnFocus() {
current = this;
graphEditor = NodeGraphEditor.GetEditor(graph);
- if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
+ if (graphEditor != null && NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
}
partial void OnEnable();
diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs
index 8d81a07..bcb95e1 100644
--- a/Scripts/Editor/NodeGraphEditor.cs
+++ b/Scripts/Editor/NodeGraphEditor.cs
@@ -28,8 +28,8 @@ namespace XNodeEditor {
return new NodeEditorPreferences.Settings();
}
- /// Returns context menu path. Returns null if node is not available.
- public virtual string GetNodePath(Type type) {
+ /// Returns context node menu path. Null or empty strings for hidden nodes.
+ public virtual string GetNodeMenuName(Type type) {
//Check if type has the CreateNodeMenuAttribute
XNode.Node.CreateNodeMenuAttribute attrib;
if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path
diff --git a/Scripts/Node.cs b/Scripts/Node.cs
index be3d5a7..7c52d9b 100644
--- a/Scripts/Node.cs
+++ b/Scripts/Node.cs
@@ -57,7 +57,7 @@ namespace XNode {
[SerializeField] public NodeGraph graph;
/// Position on the
[SerializeField] public Vector2 position;
- /// Input s. It is recommended not to modify these at hand. Instead, see
+ /// It is recommended not to modify these at hand. Instead, see and
[SerializeField] private NodePortDictionary ports = new NodePortDictionary();
protected void OnEnable() {
@@ -181,7 +181,7 @@ namespace XNode {
else return fallback;
}
- /// Returns a value based on requested port output. Should be overridden before used.
+ /// Returns a value based on requested port output. Should be overridden in all derived nodes with outputs.
/// The requested port.
public virtual object GetValue(NodePort port) {
Debug.LogWarning("No GetValue(NodePort port) override defined for " + GetType());
@@ -194,7 +194,7 @@ namespace XNode {
public virtual void OnCreateConnection(NodePort from, NodePort to) { }
/// Called after a connection is removed from this port
- /// Output Input
+ /// Output or Input
public virtual void OnRemoveConnection(NodePort port) { }
/// Disconnect everything from this node
@@ -240,7 +240,7 @@ namespace XNode {
public class CreateNodeMenuAttribute : Attribute {
public string menuName;
/// Manually supply node class with a context menu path
- /// Path to this node in the context menu
+ /// Path to this node in the context menu. Null or empty hides it.
public CreateNodeMenuAttribute(string menuName) {
this.menuName = menuName;
}
@@ -272,6 +272,16 @@ namespace XNode {
}
}
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class NodeWidth : Attribute {
+ public int width;
+ /// Specify a width for this node type
+ /// Width
+ public NodeWidth(int width) {
+ this.width = width;
+ }
+ }
+
[Serializable] private class NodePortDictionary : Dictionary, ISerializationCallbackReceiver {
[SerializeField] private List keys = new List();
[SerializeField] private List values = new List();
diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs
index 23507a6..b194230 100644
--- a/Scripts/NodeDataCache.cs
+++ b/Scripts/NodeDataCache.cs
@@ -49,12 +49,16 @@ namespace XNode {
List nodeTypes = new List();
System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
Assembly selfAssembly = Assembly.GetAssembly(baseType);
- if (selfAssembly.FullName.StartsWith("Assembly-CSharp")) {
+ if (selfAssembly.FullName.StartsWith("Assembly-CSharp") && !selfAssembly.FullName.Contains("-firstpass")) {
// If xNode is not used as a DLL, check only CSharp (fast)
nodeTypes.AddRange(selfAssembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)));
} else {
- // Else, check all DDLs (slow)
+ // Else, check all relevant DDLs (slower)
+ // ignore all unity related assemblies
foreach (Assembly assembly in assemblies) {
+ if (assembly.FullName.StartsWith("Unity")) continue;
+ // unity created assemblies always have version 0.0.0
+ if (!assembly.FullName.Contains("Version=0.0.0")) continue;
nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
}
}
diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs
index 32310d2..d6d766c 100644
--- a/Scripts/NodeGraph.cs
+++ b/Scripts/NodeGraph.cs
@@ -34,14 +34,20 @@ namespace XNode {
}
/// Safely remove a node and all its connections
- ///
+ /// The node to remove
public void RemoveNode(Node node) {
node.ClearConnections();
nodes.Remove(node);
+ if (Application.isPlaying) Destroy(node);
}
/// Remove all nodes and connections from the graph
public void Clear() {
+ if (Application.isPlaying) {
+ for (int i = 0; i < nodes.Count; i++) {
+ Destroy(nodes[i]);
+ }
+ }
nodes.Clear();
}
@@ -67,5 +73,10 @@ namespace XNode {
return graph;
}
+
+ private void OnDestroy() {
+ // Remove all nodes prior to graph destruction
+ Clear();
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs
index 65ad4be..0ada72d 100644
--- a/Scripts/NodePort.cs
+++ b/Scripts/NodePort.cs
@@ -190,7 +190,7 @@ namespace XNode {
public void Connect(NodePort port) {
if (connections == null) connections = new List();
if (port == null) { Debug.LogWarning("Cannot connect to null port"); return; }
- if (port == this) { Debug.LogWarning("Attempting to connect port to self."); return; }
+ if (port == this) { Debug.LogWarning("Cannot connect port to self."); return; }
if (IsConnectedTo(port)) { Debug.LogWarning("Port already connected. "); return; }
if (direction == port.direction) { Debug.LogWarning("Cannot connect two " + (direction == IO.Input ? "input" : "output") + " connections"); return; }
if (port.connectionType == Node.ConnectionType.Override && port.ConnectionCount != 0) { port.ClearConnections(); }