1
0
mirror of https://github.com/Siccity/xNode.git synced 2026-02-06 15:24:55 +08:00

Merge pull request #52 from zulfajuniadi/examples

Merged Example Branch with Master
This commit is contained in:
Thor Brigsted 2018-07-08 21:43:18 +02:00 committed by GitHub
commit 2675c088f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 227 additions and 50 deletions

View File

@ -8,12 +8,12 @@ namespace XNodeEditor.Examples {
public class MathGraphEditor : NodeGraphEditor {
/// <summary>
/// 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.
/// </summary>
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;
}
}

View File

@ -9,12 +9,12 @@ namespace XNodeEditor.Examples {
public class StateGraphEditor : NodeGraphEditor {
/// <summary>
/// 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.
/// </summary>
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;
}
}

View File

@ -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.

View File

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

View File

@ -203,16 +203,13 @@ namespace XNodeEditor {
EditorUtility.SetDirty(graph);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} else if (currentActivity == NodeActivity.DragNode) {
IEnumerable<XNode.Node> 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 {
}
}
/// <summary> Initiate a rename on the currently selected node </summary>
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();
}
}
/// <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;
@ -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);
}

View File

@ -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<UnityEngine.Object> selectionCache;
private List<XNode.Node> 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<UnityEngine.Object>(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<XNode.Node>();
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<XNode.NodePort, Vector2>();
//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 {
}
}
/// <summary> Returns true if outside window area </summary>
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");
}

View File

@ -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 {
/// <summary> Make a simple port field. </summary>
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);
}
/// <summary> Draws an input and an output port on the same line </summary>
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;
}
/// <summary> Draw an editable list of instance ports. Port names are named as "[fieldName] [index]" </summary>
/// <param name="fieldName">Supply a list for editable values</param>
/// <param name="type">Value type of added instance ports</param>
/// <param name="serializedObject">The serializedObject of the node</param>
/// <param name="connectionType">Connection type of added instance ports</param>
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<XNode.NodePort> 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();
}
}
}

View File

@ -13,6 +13,10 @@ namespace XNodeEditor {
public static Dictionary<Type, Color> nodeTint { get { return _nodeTint != null ? _nodeTint : _nodeTint = GetNodeTint(); } }
[NonSerialized] private static Dictionary<Type, Color> _nodeTint;
/// <summary> Custom node widths defined with [NodeWidth(width)] </summary>
public static Dictionary<Type, int> nodeWidth { get { return _nodeWidth != null ? _nodeWidth : _nodeWidth = GetNodeWidth(); } }
[NonSerialized] private static Dictionary<Type, int> _nodeWidth;
/// <summary> All available node types </summary>
public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
@ -34,6 +38,17 @@ namespace XNodeEditor {
return tints;
}
public static Dictionary<Type, int> GetNodeWidth() {
Dictionary<Type, int> widths = new Dictionary<Type, int>();
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;
}
/// <summary> Get all classes deriving from baseType via reflection </summary>
public static Type[] GetDerivedTypes(Type baseType) {
List<System.Type> types = new List<System.Type>();

View File

@ -11,8 +11,8 @@ namespace XNodeEditor {
/// <summary> Stores node positions for all nodePorts. </summary>
public Dictionary<XNode.NodePort, Rect> portConnectionPoints { get { return _portConnectionPoints; } }
private Dictionary<XNode.NodePort, Rect> _portConnectionPoints = new Dictionary<XNode.NodePort, Rect>();
public Dictionary<XNode.Node, float> nodeWidths { get { return _nodeWidths; } }
private Dictionary<XNode.Node, float> _nodeWidths = new Dictionary<XNode.Node, float>();
public Dictionary<XNode.Node, Vector2> nodeSizes { get { return _nodeSizes; } }
private Dictionary<XNode.Node, Vector2> _nodeSizes = new Dictionary<XNode.Node, Vector2>();
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();

View File

@ -28,8 +28,8 @@ namespace XNodeEditor {
return new NodeEditorPreferences.Settings();
}
/// <summary> Returns context menu path. Returns null if node is not available. </summary>
public virtual string GetNodePath(Type type) {
/// <summary> Returns context node menu path. Null or empty strings for hidden nodes. </summary>
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

View File

@ -57,7 +57,7 @@ namespace XNode {
[SerializeField] public NodeGraph graph;
/// <summary> Position on the <see cref="NodeGraph"/> </summary>
[SerializeField] public Vector2 position;
/// <summary> Input <see cref="NodePort"/>s. It is recommended not to modify these at hand. Instead, see <see cref="InputAttribute"/> </summary>
/// <summary> It is recommended not to modify these at hand. Instead, see <see cref="InputAttribute"/> and <see cref="OutputAttribute"/> </summary>
[SerializeField] private NodePortDictionary ports = new NodePortDictionary();
protected void OnEnable() {
@ -181,7 +181,7 @@ namespace XNode {
else return fallback;
}
/// <summary> Returns a value based on requested port output. Should be overridden before used. </summary>
/// <summary> Returns a value based on requested port output. Should be overridden in all derived nodes with outputs. </summary>
/// <param name="port">The requested port.</param>
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) { }
/// <summary> Called after a connection is removed from this port </summary>
/// <param name="from">Output</param> <param name="to">Input</param>
/// <param name="port">Output or Input</param>
public virtual void OnRemoveConnection(NodePort port) { }
/// <summary> Disconnect everything from this node </summary>
@ -240,7 +240,7 @@ namespace XNode {
public class CreateNodeMenuAttribute : Attribute {
public string menuName;
/// <summary> Manually supply node class with a context menu path </summary>
/// <param name="menuName"> Path to this node in the context menu </param>
/// <param name="menuName"> Path to this node in the context menu. Null or empty hides it. </param>
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;
/// <summary> Specify a width for this node type </summary>
/// <param name="width"> Width </param>
public NodeWidth(int width) {
this.width = width;
}
}
[Serializable] private class NodePortDictionary : Dictionary<string, NodePort>, ISerializationCallbackReceiver {
[SerializeField] private List<string> keys = new List<string>();
[SerializeField] private List<NodePort> values = new List<NodePort>();

View File

@ -49,12 +49,16 @@ namespace XNode {
List<System.Type> nodeTypes = new List<System.Type>();
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());
}
}

View File

@ -34,14 +34,20 @@ namespace XNode {
}
/// <summary> Safely remove a node and all its connections </summary>
/// <param name="node"></param>
/// <param name="node"> The node to remove </param>
public void RemoveNode(Node node) {
node.ClearConnections();
nodes.Remove(node);
if (Application.isPlaying) Destroy(node);
}
/// <summary> Remove all nodes and connections from the graph </summary>
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();
}
}
}

View File

@ -190,7 +190,7 @@ namespace XNode {
public void Connect(NodePort port) {
if (connections == null) connections = new List<PortConnection>();
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(); }