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 @@ +![alt text](https://user-images.githubusercontent.com/37786733/41541140-71602302-731a-11e8-9434-79b3a57292b6.png) + [![Discord](https://img.shields.io/discord/361769369404964864.svg)](https://discord.gg/qgPrHv4) [![GitHub issues](https://img.shields.io/github/issues/Siccity/xNode.svg)](https://github.com/Siccity/xNode/issues) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](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(); }