diff --git a/Examples/NodeToy/NodeGraphExample.asset b/Examples/NodeToy/NodeGraphExample.asset index 595fa1a..c44ea50 100644 Binary files a/Examples/NodeToy/NodeGraphExample.asset and b/Examples/NodeToy/NodeGraphExample.asset differ diff --git a/Examples/NodeToy/NodeGraphExample.asset.meta b/Examples/NodeToy/NodeGraphExample.asset.meta index 1af7b94..d3a3583 100644 --- a/Examples/NodeToy/NodeGraphExample.asset.meta +++ b/Examples/NodeToy/NodeGraphExample.asset.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 2e27fbb85ccd5994e932fd8a6d34e4b3 -timeCreated: 1506790922 +guid: ce9064d2750a8344784c005788637f15 +timeCreated: 1507333474 licenseType: Free NativeFormatImporter: mainObjectFileID: 11400000 diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 536f759..aaf5b93 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -93,6 +93,8 @@ public partial class NodeEditorWindow { //If connection is valid, save it if (draggedOutputTarget != null) { if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget); + EditorUtility.SetDirty(graph); + AssetDatabase.SaveAssets(); } //Release dragged connection draggedOutput = null; diff --git a/Scripts/Node.cs b/Scripts/Node.cs index e972308..d563231 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -5,23 +5,24 @@ using System; /// Base class for all nodes [Serializable] -public abstract class Node { +public abstract class Node : ScriptableObject { /// Name of the node - [SerializeField] public string name = ""; - [SerializeField] public NodeGraph graph; + [NonSerialized] public NodeGraph graph; [SerializeField] public Rect rect = new Rect(0,0,200,200); /// Input s. It is recommended not to modify these at hand. Instead, see - [SerializeField] public NodePort[] inputs = new NodePort[0]; + [SerializeField] public List inputs = new List(); /// Output s. It is recommended not to modify these at hand. Instead, see [SerializeField] public NodePort[] outputs = new NodePort[0]; - public int InputCount { get { return inputs.Length; } } + public int InputCount { get { return inputs.Count; } } public int OutputCount { get { return outputs.Length; } } - /// Constructor protected Node() { CachePorts(); //Cache the ports at creation time so we don't have to use reflection at runtime + } + + protected void OnEnable() { Init(); } @@ -33,7 +34,7 @@ public abstract class Node { public virtual void OnCreateConnection(NodePort from, NodePort to) { } public int GetInputId(NodePort input) { - for (int i = 0; i < inputs.Length; i++) { + for (int i = 0; i < inputs.Count; i++) { if (input == inputs[i]) return i; } @@ -55,7 +56,7 @@ public abstract class Node { } public void ClearConnections() { - for (int i = 0; i < inputs.Length; i++) { + for (int i = 0; i < inputs.Count; i++) { inputs[i].ClearConnections(); } for (int i = 0; i < outputs.Length; i++) { @@ -99,7 +100,7 @@ public abstract class Node { else if (outputAttrib != null) outputPorts.Add(new NodePort(fieldInfo[i].Name, fieldInfo[i].FieldType, this, NodePort.IO.Output)); } - inputs = inputPorts.ToArray(); + inputs = inputPorts; outputs = outputPorts.ToArray(); } } diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index 128add7..d61ec99 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -5,7 +5,8 @@ using System; /// Base class for all node graphs [Serializable] -public abstract class NodeGraph : ScriptableObject { +public abstract class NodeGraph : ScriptableObject, ISerializationCallbackReceiver { + /// All nodes in the graph. /// See: [SerializeField] public List nodes = new List(); @@ -15,7 +16,10 @@ public abstract class NodeGraph : ScriptableObject { } public virtual Node AddNode(Type type) { - Node node = (Node)Activator.CreateInstance(type); + Node node = ScriptableObject.CreateInstance(type) as Node; +#if UNITY_EDITOR + UnityEditor.AssetDatabase.AddObjectToAsset(node, this); +#endif nodes.Add(node); node.graph = this; return node; @@ -32,5 +36,14 @@ public abstract class NodeGraph : ScriptableObject { public void Clear() { nodes.Clear(); } + + public void OnBeforeSerialize() { + } + + public void OnAfterDeserialize() { + for (int i = 0; i < nodes.Count; i++) { + nodes[i].graph = this; + } + } } diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index bf626e5..c0df2bb 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -9,9 +9,7 @@ public class NodePort { public int ConnectionCount { get { return connections.Count; } } /// Return the first connection - public NodePort Connection { get { return connections.Count > 0 ? connections[0] : null; } } - /// Returns a copy of the connections list - public List Connections { get { return new List(connections); } } + public NodePort Connection { get { return connections.Count > 0 ? connections[0].Port : null; } } public IO direction { get { return _direction; } } /// Is this port connected to anytihng? @@ -20,11 +18,13 @@ public class NodePort { public bool IsOutput { get { return direction == IO.Output; } } public Node node { get; private set; } - public string name { get { return _name; } set { _name = value; } } + public string name { get { return _name; } } public bool enabled { get { return _enabled; } set { _enabled = value; } } + public string id { get { return _id; } } - [SerializeField] private List connections = new List(); - + [SerializeField] private List connections = new List(); + [SerializeField] private string asdf; + [SerializeField] private string _id; [SerializeField] public Type type; [SerializeField] private string _name; [SerializeField] private bool _enabled = true; @@ -35,39 +35,75 @@ public class NodePort { this.type = type; this.node = node; _direction = direction; + _id = node.GetInstanceID() + _name; } /// Connect this to another /// The to connect to public void Connect(NodePort port) { - if (connections == null) connections = new List(); + 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 (connections.Contains(port)) { Debug.LogWarning("Port already connected. "); 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; } - connections.Add(port); - port.connections.Add(this); + connections.Add(new PortConnection(port)); + if (port.connections == null) port.connections = new List(); + port.connections.Add(new PortConnection(this)); node.OnCreateConnection(this, port); port.node.OnCreateConnection(this, port); } public NodePort GetConnection(int i) { - return connections[i]; + return connections[i].Port; } public bool IsConnectedTo(NodePort port) { - return connections.Contains(port); + for (int i = 0; i < connections.Count; i++) { + if (connections[i].Port == port) return true; + } + return false; } public void Disconnect(NodePort port) { - connections.Remove(port); - port.connections.Remove(this); + for (int i = 0; i < connections.Count; i++) { + if (connections[i].Port == port) { + connections.RemoveAt(i); + } + } + for (int i = 0; i < port.connections.Count; i++) { + if (port.connections[i].Port == this) { + port.connections.RemoveAt(i); + } + } } public void ClearConnections() { for (int i = 0; i < connections.Count; i++) { - connections[i].connections.Remove(this); + Disconnect(connections[i].Port); + } + } + + [Serializable] + public class PortConnection { + [SerializeField] public Node node; + [SerializeField] public string portID; + public NodePort Port { get { return port != null ? port : port = GetPort(); } } + [NonSerialized] private NodePort port; + + public PortConnection(NodePort port) { + this.port = port; + node = port.node; + portID = port.id; + } + + private NodePort GetPort() { + for (int i = 0; i < node.OutputCount; i++) { + if (node.outputs[i].id == portID) return node.outputs[i]; + } + for (int i = 0; i < node.InputCount; i++) { + if (node.inputs[i].id == portID) return node.inputs[i]; + } + return null; } - connections.Clear(); } }