diff --git a/.gitignore b/.gitignore index 2946d78..46cc792 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ # Unity3D Generated File On Crash Reports sysinfo.txt +/Examples/ README.md.meta LICENSE.md.meta diff --git a/README.md b/README.md index 5517e75..5194a63 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Siccity/xNode/master/LICENSE.md) [![GitHub Wiki](https://img.shields.io/badge/wiki-available-brightgreen.svg)](https://github.com/Siccity/xNode/wiki) -[Go to Downloads](https://github.com/Siccity/xNode/releases) +[Go to Downloads](https://github.com/Siccity/xNode/releases) / [Go to Asset Store](http://u3d.as/108S) ### 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 5ce6cc3..39febe9 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -8,7 +8,7 @@ namespace XNodeEditor { /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. [CustomNodeEditor(typeof(XNode.Node))] - public class NodeEditor : XNodeInternal.NodeEditorBase { + public class NodeEditor : XNodeEditor.Internal.NodeEditorBase { /// Fires every whenever a node was modified through the editor public static Action onUpdateNode; @@ -54,7 +54,7 @@ namespace XNodeEditor { [AttributeUsage(AttributeTargets.Class)] public class CustomNodeEditorAttribute : Attribute, - XNodeInternal.NodeEditorBase.INodeEditorAttrib { + XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { private Type inspectedType; /// Tells a NodeEditor which Node type it is an editor for /// Type that this editor can edit diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 41bb196..946e43a 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEditor; using UnityEngine; @@ -46,7 +47,11 @@ namespace XNodeEditor { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; node.position = WindowToGridPosition(e.mousePosition) + dragOffset[i]; - if (NodeEditorPreferences.GridSnap) { + bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; + if (e.control) { + gridSnap = !gridSnap; + } + if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } @@ -197,16 +202,42 @@ namespace XNodeEditor { } } - // Dublicate selected nodes and select the dublicates + /// Dublicate selected nodes and select the dublicates public void DublicateSelectedNodes() { UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length]; + Dictionary substitutes = new Dictionary(); 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; - if (node.graph != graph) continue; // ignore nodes selected in another graph - XNode.Node n = graph.CopyNode(node); - n.position = node.position + new Vector2(30, 30); - newNodes[i] = n; + XNode.Node srcNode = Selection.objects[i] as XNode.Node; + if (srcNode.graph != graph) continue; // ignore nodes selected in another graph + XNode.Node newNode = graph.CopyNode(srcNode); + substitutes.Add(srcNode, newNode); + newNode.position = srcNode.position + new Vector2(30, 30); + newNodes[i] = newNode; + } + } + + // Walk through the selected nodes again, recreate connections, using the new nodes + for (int i = 0; i < Selection.objects.Length; i++) { + if (Selection.objects[i] is XNode.Node) { + XNode.Node srcNode = Selection.objects[i] as XNode.Node; + if (srcNode.graph != graph) continue; // ignore nodes selected in another graph + foreach (XNode.NodePort port in srcNode.Ports) { + for (int c = 0; c < port.ConnectionCount; c++) { + XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); + XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); + + if (substitutes.ContainsKey(inputPort.node) && substitutes.ContainsKey(outputPort.node)) { + XNode.Node newNodeIn = substitutes[inputPort.node]; + XNode.Node newNodeOut = substitutes[outputPort.node]; + newNodeIn.UpdateStaticPorts(); + newNodeOut.UpdateStaticPorts(); + inputPort = newNodeIn.GetInputPort(inputPort.fieldName); + outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); + } + if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort); + } + } } } Selection.objects = newNodes; diff --git a/Scripts/Editor/NodeEditorBase.cs b/Scripts/Editor/NodeEditorBase.cs index 67d4833..0868ce0 100644 --- a/Scripts/Editor/NodeEditorBase.cs +++ b/Scripts/Editor/NodeEditorBase.cs @@ -5,11 +5,12 @@ using System.Reflection; using UnityEngine; using UnityEditor; -namespace XNodeInternal { +namespace XNodeEditor.Internal { /// Handles caching of custom editor classes and their target types. Accessible with GetEditor(Type type) public class NodeEditorBase where A : Attribute, NodeEditorBase.INodeEditorAttrib where T : NodeEditorBase where K : ScriptableObject { /// Custom editors defined with [CustomNodeEditor] private static Dictionary editors; + private static Dictionary serializeds; public K target; public SerializedObject serializedObject; @@ -18,10 +19,17 @@ namespace XNodeInternal { Type type = target.GetType(); T editor = GetEditor(type); editor.target = target; - editor.serializedObject = new SerializedObject(target); + editor.serializedObject = GetSerialized(target); return editor; } + private static SerializedObject GetSerialized(K target) { + if (target == null) return null; + if (serializeds == null) serializeds = new Dictionary(); + if (!serializeds.ContainsKey(target)) serializeds.Add(target, new SerializedObject(target)); + return serializeds[target]; + } + private static T GetEditor(Type type) { if (type == null) return null; if (editors == null) CacheCustomEditors(); diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index e3f8c4c..2843151 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -6,14 +6,14 @@ using UnityEngine; namespace XNodeEditor { /// Contains GUI methods public partial class NodeEditorWindow { - private NodeGraphEditor currentGraphEditor; + public NodeGraphEditor graphEditor; private List selectionCache; private void OnGUI() { Event e = Event.current; Matrix4x4 m = GUI.matrix; if (graph == null) return; - currentGraphEditor = NodeGraphEditor.GetEditor(graph); + graphEditor = NodeGraphEditor.GetEditor(graph); Controls(); @@ -52,8 +52,8 @@ namespace XNodeEditor { rect.position = Vector2.zero; Vector2 center = rect.size / 2f; - Texture2D gridTex = currentGraphEditor.GetGridTexture(); - Texture2D crossTex = currentGraphEditor.GetSecondaryGridTexture(); + Texture2D gridTex = graphEditor.GetGridTexture(); + Texture2D crossTex = graphEditor.GetSecondaryGridTexture(); // Offset from origin in tile units float xOffset = -(center.x * zoom + panOffset.x) / gridTex.width; @@ -122,7 +122,7 @@ namespace XNodeEditor { Type type = nodeTypes[i]; //Get node context menu path - string path = currentGraphEditor.GetNodePath(type); + string path = graphEditor.GetNodePath(type); if (path == null) continue; contextMenu.AddItem(new GUIContent(path), false, () => { @@ -151,15 +151,50 @@ namespace XNodeEditor { startPoint = GridToWindowPosition(startPoint); endPoint = GridToWindowPosition(endPoint); - Vector2 startTangent = startPoint; - if (startPoint.x < endPoint.x) startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, 0.7f); - else startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, -0.7f); + switch (NodeEditorPreferences.GetSettings().noodleType) { + case NodeEditorPreferences.NoodleType.Curve: + Vector2 startTangent = startPoint; + if (startPoint.x < endPoint.x) startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, 0.7f); + else startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, -0.7f); - Vector2 endTangent = endPoint; - if (startPoint.x > endPoint.x) endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, -0.7f); - else endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, 0.7f); - - Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, col, null, 4); + Vector2 endTangent = endPoint; + if (startPoint.x > endPoint.x) endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, -0.7f); + else endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, 0.7f); + Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, col, null, 4); + break; + case NodeEditorPreferences.NoodleType.Line: + Handles.color = col; + Handles.DrawAAPolyLine(5, startPoint, endPoint); + break; + case NodeEditorPreferences.NoodleType.Angled: + Handles.color = col; + if (startPoint.x <= endPoint.x - (50 / zoom)) { + float midpoint = (startPoint.x + endPoint.x) * 0.5f; + Vector2 start_1 = startPoint; + Vector2 end_1 = endPoint; + start_1.x = midpoint; + end_1.x = midpoint; + Handles.DrawAAPolyLine(5, startPoint, start_1); + Handles.DrawAAPolyLine(5, start_1, end_1); + Handles.DrawAAPolyLine(5, end_1, endPoint); + } else { + float midpoint = (startPoint.y + endPoint.y) * 0.5f; + Vector2 start_1 = startPoint; + Vector2 end_1 = endPoint; + start_1.x += 25 / zoom; + end_1.x -= 25 / zoom; + Vector2 start_2 = start_1; + Vector2 end_2 = end_1; + start_2.y = midpoint; + end_2.y = midpoint; + Handles.DrawAAPolyLine(5, startPoint, start_1); + Handles.DrawAAPolyLine(5, start_1, start_2); + Handles.DrawAAPolyLine(5, start_2, end_2); + Handles.DrawAAPolyLine(5, end_2, end_1); + Handles.DrawAAPolyLine(5, end_1, endPoint); + } + break; + } } /// Draws all connections @@ -179,7 +214,7 @@ namespace XNodeEditor { if (!input.IsConnectedTo(output)) input.Connect(output); if (!_portConnectionPoints.ContainsKey(input)) continue; Vector2 to = _portConnectionPoints[input].center; - Color connectionColor = currentGraphEditor.GetTypeColor(output.ValueType); + Color connectionColor = graphEditor.GetTypeColor(output.ValueType); DrawConnection(from, to, connectionColor); } } @@ -213,7 +248,7 @@ namespace XNodeEditor { hoveredPort = null; } - List preSelection = new List(preBoxSelection); + List preSelection = preBoxSelection != null ? new List(preBoxSelection) : new List(); //Save guiColor so we can revert it Color guiColor = GUI.color; @@ -240,7 +275,7 @@ namespace XNodeEditor { style.padding = new RectOffset(); GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(new GUIStyle(style)); - GUI.color = NodeEditorPreferences.HighlightColor; + GUI.color = NodeEditorPreferences.GetSettings().highlightColor; GUILayout.BeginVertical(new GUIStyle(highlightStyle)); } else { GUIStyle style = NodeEditorResources.styles.nodeBody; diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 1751d78..0a377f9 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -92,7 +92,7 @@ namespace XNodeEditor { Color backgroundColor = new Color32(90, 97, 105, 255); if (NodeEditorWindow.nodeTint.ContainsKey(port.node.GetType())) backgroundColor *= NodeEditorWindow.nodeTint[port.node.GetType()]; - Color col = NodeGraphEditor.GetEditor(port.node.graph).GetTypeColor(port.ValueType); + Color col = NodeEditorWindow.current.graphEditor.GetTypeColor(port.ValueType); DrawPortHandle(rect, backgroundColor, col); // Register the handle position @@ -119,7 +119,7 @@ namespace XNodeEditor { Color backgroundColor = new Color32(90, 97, 105, 255); if (NodeEditorWindow.nodeTint.ContainsKey(port.node.GetType())) backgroundColor *= NodeEditorWindow.nodeTint[port.node.GetType()]; - Color col = NodeGraphEditor.GetEditor(port.node.graph).GetTypeColor(port.ValueType); + Color col = NodeEditorWindow.current.graphEditor.GetTypeColor(port.ValueType); DrawPortHandle(rect, backgroundColor, col); // Register the handle position diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 0df1ad2..aa87785 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -5,38 +5,44 @@ using UnityEngine; namespace XNodeEditor { public static class NodeEditorPreferences { + public enum NoodleType { Curve, Line, Angled } - public static Texture2D gridTexture { - get { - VerifyLoaded(); - if (_gridTexture == null) _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); - return _gridTexture; - } - } - private static Texture2D _gridTexture; - public static Texture2D crossTexture { - get { - VerifyLoaded(); - if (_crossTexture == null) _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); - return _crossTexture; - } - } - private static Texture2D _crossTexture; - - public static bool GridSnap { get { VerifyLoaded(); return settings.gridSnap; } } - public static Color HighlightColor { get { VerifyLoaded(); return settings.highlightColor; } } + /// The last editor we checked. This should be the one we modify + private static XNodeEditor.NodeGraphEditor lastEditor; + /// The last key we checked. This should be the one we modify + private static string lastKey = "xNode.Settings"; private static Dictionary typeColors = new Dictionary(); - private static Settings settings; + private static Dictionary settings = new Dictionary(); [System.Serializable] - private class Settings : ISerializationCallbackReceiver { - public Color32 gridLineColor = new Color(0.45f, 0.45f, 0.45f); - public Color32 gridBgColor = new Color(0.18f, 0.18f, 0.18f); - public Color32 highlightColor = new Color32(255, 223, 255, 255); + public class Settings : ISerializationCallbackReceiver { + [SerializeField] private Color32 _gridLineColor = new Color(0.45f, 0.45f, 0.45f); + public Color32 gridLineColor { get { return _gridLineColor; } set { _gridLineColor = value; _gridTexture = null; _crossTexture = null; } } + + [SerializeField] private Color32 _gridBgColor = new Color(0.18f, 0.18f, 0.18f); + public Color32 gridBgColor { get { return _gridBgColor; } set { _gridBgColor = value; _gridTexture = null; } } + + public Color32 highlightColor = new Color32(255, 255, 255, 255); public bool gridSnap = true; - public string typeColorsData = ""; - public Dictionary typeColors = new Dictionary(); + [SerializeField] private string typeColorsData = ""; + [NonSerialized] public Dictionary typeColors = new Dictionary(); + public NoodleType noodleType = NoodleType.Curve; + + private Texture2D _gridTexture; + public Texture2D gridTexture { + get { + if (_gridTexture == null) _gridTexture = NodeEditorResources.GenerateGridTexture(gridLineColor, gridBgColor); + return _gridTexture; + } + } + private Texture2D _crossTexture; + public Texture2D crossTexture { + get { + if (_crossTexture == null) _crossTexture = NodeEditorResources.GenerateCrossTexture(gridLineColor); + return _crossTexture; + } + } public void OnAfterDeserialize() { // Deserialize typeColorsData @@ -59,19 +65,34 @@ namespace XNodeEditor { } } + /// Get settings of current active editor + public static Settings GetSettings() { + if (lastEditor != XNodeEditor.NodeEditorWindow.current.graphEditor) { + object[] attribs = XNodeEditor.NodeEditorWindow.current.graphEditor.GetType().GetCustomAttributes(typeof(XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute), true); + if (attribs.Length == 1) { + XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute attrib = attribs[0] as XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute; + lastEditor = XNodeEditor.NodeEditorWindow.current.graphEditor; + lastKey = attrib.editorPrefsKey; + VerifyLoaded(); + } else return null; + } + return settings[lastKey]; + } + [PreferenceItem("Node Editor")] private static void PreferencesGUI() { VerifyLoaded(); + Settings settings = NodeEditorPreferences.settings[lastKey]; - NodeSettingsGUI(); - GridSettingsGUI(); - TypeColorsGUI(); + NodeSettingsGUI(lastKey, settings); + GridSettingsGUI(lastKey, settings); + TypeColorsGUI(lastKey, settings); if (GUILayout.Button(new GUIContent("Set Default", "Reset all values to default"), GUILayout.Width(120))) { ResetPrefs(); } } - private static void GridSettingsGUI() { + private static void GridSettingsGUI(string key, Settings settings) { //Label EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel); settings.gridSnap = EditorGUILayout.Toggle("Snap", settings.gridSnap); @@ -79,75 +100,74 @@ namespace XNodeEditor { settings.gridLineColor = EditorGUILayout.ColorField("Color", settings.gridLineColor); settings.gridBgColor = EditorGUILayout.ColorField(" ", settings.gridBgColor); if (GUI.changed) { - SavePrefs(); - _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); - _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); + SavePrefs(key, settings); + NodeEditorWindow.RepaintAll(); } EditorGUILayout.Space(); } - private static void NodeSettingsGUI() { + private static void NodeSettingsGUI(string key, Settings settings) { //Label EditorGUILayout.LabelField("Node", EditorStyles.boldLabel); settings.highlightColor = EditorGUILayout.ColorField("Selection", settings.highlightColor); + settings.noodleType = (NoodleType) EditorGUILayout.EnumPopup("Noodle type", (Enum) settings.noodleType); if (GUI.changed) { - SavePrefs(); + SavePrefs(key, settings); NodeEditorWindow.RepaintAll(); } EditorGUILayout.Space(); } - private static void TypeColorsGUI() { + private static void TypeColorsGUI(string key, Settings settings) { //Label EditorGUILayout.LabelField("Types", EditorStyles.boldLabel); //Display type colors. Save them if they are edited by the user - List keys = new List(typeColors.Keys); - foreach (string key in keys) { - Color col = typeColors[key]; + List typeColorKeys = new List(typeColors.Keys); + foreach (string typeColorKey in typeColorKeys) { + Color col = typeColors[typeColorKey]; EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); - col = EditorGUILayout.ColorField(key, col); + col = EditorGUILayout.ColorField(typeColorKey, col); EditorGUILayout.EndHorizontal(); if (EditorGUI.EndChangeCheck()) { - typeColors[key] = col; - if (settings.typeColors.ContainsKey(key)) settings.typeColors[key] = col; - else settings.typeColors.Add(key, col); - SavePrefs(); + typeColors[typeColorKey] = col; + if (settings.typeColors.ContainsKey(typeColorKey)) settings.typeColors[typeColorKey] = col; + else settings.typeColors.Add(typeColorKey, col); + SavePrefs(typeColorKey, settings); NodeEditorWindow.RepaintAll(); } } } + /// Load prefs if they exist. Create if they don't private static Settings LoadPrefs() { - // Remove obsolete editorprefs - if (EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.DeleteKey("xnode_typecolors"); - if (EditorPrefs.HasKey("xnode_gridcolor0")) EditorPrefs.DeleteKey("xnode_gridcolor0"); - if (EditorPrefs.HasKey("xnode_gridcolor1")) EditorPrefs.DeleteKey("xnode_gridcolor1"); - if (EditorPrefs.HasKey("xnode_gridsnap")) EditorPrefs.DeleteKey("xnode_gridcolor1"); - - if (!EditorPrefs.HasKey("xNode.Settings")) EditorPrefs.SetString("xNode.Settings", JsonUtility.ToJson(new Settings())); - return JsonUtility.FromJson(EditorPrefs.GetString("xNode.Settings")); + // Create settings if it doesn't exist + if (!EditorPrefs.HasKey(lastKey)) { + if (lastEditor != null) EditorPrefs.SetString(lastKey, JsonUtility.ToJson(lastEditor.GetDefaultPreferences())); + else EditorPrefs.SetString(lastKey, JsonUtility.ToJson(new Settings())); + } + return JsonUtility.FromJson(EditorPrefs.GetString(lastKey)); } /// Delete all prefs public static void ResetPrefs() { - if (EditorPrefs.HasKey("xNode.Settings")) EditorPrefs.DeleteKey("xNode.Settings"); - - settings = LoadPrefs(); + if (EditorPrefs.HasKey(lastKey)) EditorPrefs.DeleteKey(lastKey); + if (settings.ContainsKey(lastKey)) settings.Remove(lastKey); typeColors = new Dictionary(); - _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); - _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); + VerifyLoaded(); NodeEditorWindow.RepaintAll(); } - private static void SavePrefs() { - EditorPrefs.SetString("xNode.Settings", JsonUtility.ToJson(settings)); + /// Save preferences in EditorPrefs + private static void SavePrefs(string key, Settings settings) { + EditorPrefs.SetString(key, JsonUtility.ToJson(settings)); } + /// Check if we have loaded settings for given key. If not, load them private static void VerifyLoaded() { - if (settings == null) settings = LoadPrefs(); + if (!settings.ContainsKey(lastKey)) settings.Add(lastKey, LoadPrefs()); } /// Return color based on type @@ -156,7 +176,7 @@ namespace XNodeEditor { if (type == null) return Color.gray; string typeName = type.PrettyName(); if (!typeColors.ContainsKey(typeName)) { - if (settings.typeColors.ContainsKey(typeName)) typeColors.Add(typeName, settings.typeColors[typeName]); + if (settings[lastKey].typeColors.ContainsKey(typeName)) typeColors.Add(typeName, settings[lastKey].typeColors[typeName]); else { #if UNITY_5_4_OR_NEWER UnityEngine.Random.InitState(typeName.GetHashCode()); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 2842e61..3fa9406 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -7,16 +7,21 @@ using UnityEngine; namespace XNodeEditor { /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. [CustomNodeGraphEditor(typeof(XNode.NodeGraph))] - public class NodeGraphEditor : XNodeInternal.NodeEditorBase { + public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase { /// Custom node editors defined with [CustomNodeGraphEditor] [NonSerialized] private static Dictionary editors; public virtual Texture2D GetGridTexture() { - return NodeEditorPreferences.gridTexture; + return NodeEditorPreferences.GetSettings().gridTexture; } public virtual Texture2D GetSecondaryGridTexture() { - return NodeEditorPreferences.crossTexture; + return NodeEditorPreferences.GetSettings().crossTexture; + } + + /// Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. + public virtual NodeEditorPreferences.Settings GetDefaultPreferences() { + return new NodeEditorPreferences.Settings(); } /// Returns context menu path. Returns null if node is not available. @@ -35,13 +40,15 @@ namespace XNodeEditor { [AttributeUsage(AttributeTargets.Class)] public class CustomNodeGraphEditorAttribute : Attribute, - XNodeInternal.NodeEditorBase.INodeEditorAttrib { + XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { private Type inspectedType; - /// Tells a NodeEditor which Node type it is an editor for + public string editorPrefsKey; + /// Tells a NodeGraphEditor which Graph type it is an editor for /// Type that this editor can edit - /// Path to the node - public CustomNodeGraphEditorAttribute(Type inspectedType) { + /// Define unique key for unique layout settings instance + public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") { this.inspectedType = inspectedType; + this.editorPrefsKey = editorPrefsKey; } public Type GetInspectedType() { diff --git a/Scripts/Node.cs b/Scripts/Node.cs index c1883d3..0e0449f 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -61,10 +61,15 @@ namespace XNode { [SerializeField] private NodePortDictionary ports = new NodePortDictionary(); protected void OnEnable() { - NodeDataCache.UpdatePorts(this, ports); + UpdateStaticPorts(); Init(); } + /// Update static ports to reflect class fields. This happens automatically on enable. + public void UpdateStaticPorts() { + NodeDataCache.UpdatePorts(this, ports); + } + /// Initialize node. Called on creation. protected virtual void Init() { name = GetType().Name; } @@ -124,7 +129,8 @@ namespace XNode { /// Removes all instance ports from the node [ContextMenu("Clear instance ports")] public void ClearInstancePorts() { - foreach (NodePort port in InstancePorts) { + List instancePorts = new List(InstancePorts); + foreach (NodePort port in instancePorts) { RemoveInstancePort(port); } } @@ -196,22 +202,18 @@ namespace XNode { foreach (NodePort port in Ports) port.ClearConnections(); } - public override int GetHashCode() { - return JsonUtility.ToJson(this).GetHashCode(); - } - /// Mark a serializable field as an input port. You can access this through [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public class InputAttribute : Attribute { public ShowBackingValue backingValue; public ConnectionType connectionType; - + /// Mark a serializable field as an input port. You can access this through /// Should we display the backing value for this port as an editor field? /// Should we allow multiple connections? public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple) { this.backingValue = backingValue; - this.connectionType = connectionType; + this.connectionType = connectionType; } }