Esempio n. 1
0
        /// <summary>
        /// Creates a working copy of the specified nodeCanvas, and optionally also of it's associated editorStates.
        /// This breaks the link of this object to any stored assets and references. That means, that all changes to this object will have to be explicitly saved.
        /// </summary>
        public static NodeCanvas CreateWorkingCopy(NodeCanvas nodeCanvas, bool editorStates = true)
        {
            if (nodeCanvas == null)
            {
                return(null);
            }

            // Lists holding initial and cloned version of each ScriptableObject for later replacement of references
            List <ScriptableObject> allSOs    = new List <ScriptableObject> ();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject> ();

            System.Func <ScriptableObject, ScriptableObject> copySOs = (ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so);

            // Clone and enter the canvas object and it's referenced SOs
            nodeCanvas = AddClonedSO(allSOs, clonedSOs, nodeCanvas);
            AddClonedSOs(allSOs, clonedSOs, nodeCanvas.GetScriptableObjects());

            // Iterate over the core ScriptableObjects in the canvas and clone them
            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {
                Node node = nodeCanvas.nodes[nodeCnt];

                // Clone Node and additional scriptableObjects
                Node clonedNode = AddClonedSO(allSOs, clonedSOs, node);
                AddClonedSOs(allSOs, clonedSOs, clonedNode.GetScriptableObjects());

                // Update representative port list connectionPorts with all ports and clone them
                ConnectionPortManager.UpdatePortLists(clonedNode);
                AddClonedSOs(allSOs, clonedSOs, clonedNode.connectionPorts);
            }

            // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list
            nodeCanvas.CopyScriptableObjects(copySOs);

            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {             // Replace SOs in all Nodes
                Node node = nodeCanvas.nodes[nodeCnt];

                // Replace node and additional ScriptableObjects with their copies
                Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, node);
                clonedNode.CopyScriptableObjects(copySOs);

                // Replace ConnectionPorts
                foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(clonedNode, true))
                {                 // Iterate over each static port declaration and replace it with it's connections
                    ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(clonedNode);
                    port = ReplaceSO(allSOs, clonedSOs, port);
                    for (int c = 0; c < port.connections.Count; c++)
                    {
                        port.connections[c] = ReplaceSO(allSOs, clonedSOs, port.connections[c]);
                    }
                    port.body = clonedNode;
                    portDecl.portField.SetValue(clonedNode, port);
                }
                for (int i = 0; i < clonedNode.dynamicConnectionPorts.Count; i++)
                {                 // Iterate over all dynamic connection ports
                    ConnectionPort port = clonedNode.dynamicConnectionPorts[i];
                    port = ReplaceSO(allSOs, clonedSOs, port);
                    for (int c = 0; c < port.connections.Count; c++)
                    {
                        port.connections[c] = ReplaceSO(allSOs, clonedSOs, port.connections[c]);
                    }
                    port.body = clonedNode;
                    clonedNode.dynamicConnectionPorts[i] = port;
                }
            }

            if (editorStates)             // Clone the editorStates
            {
                nodeCanvas.editorStates = CreateWorkingCopy(nodeCanvas.editorStates, nodeCanvas);
            }
            // Fix references in editorStates to Nodes in the canvas
            foreach (NodeEditorState state in nodeCanvas.editorStates)
            {
                state.selectedNode = ReplaceSO(allSOs, clonedSOs, state.selectedNode);
            }

            return(nodeCanvas);
        }
Esempio n. 2
0
        /// <summary>
        /// Creates a working copy of the specified nodeCanvas, and optionally also of it's associated editorStates.
        /// This breaks the link of this object to any stored assets and references. That means, that all changes to this object will have to be explicitly saved.
        /// </summary>
        public static NodeCanvas CreateWorkingCopy(NodeCanvas nodeCanvas, bool editorStates)
        {
            nodeCanvas.Validate();
            nodeCanvas = Clone(nodeCanvas);

            // Take each SO, make a clone of it and store both versions in the respective list
            // This will only iterate over the 'source instances'
            List <ScriptableObject> allSOs    = new List <ScriptableObject> ();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject> ();

            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {
                Node node = nodeCanvas.nodes[nodeCnt];
                node.CheckNodeKnobMigration();

                // Clone Node and additional scriptableObjects
                Node clonedNode = AddClonedSO(allSOs, clonedSOs, node);
                AddClonedSOs(allSOs, clonedSOs, clonedNode.GetScriptableObjects());

                foreach (NodeKnob knob in clonedNode.nodeKnobs)
                {                 // Clone NodeKnobs and additional scriptableObjects
                    AddClonedSO(allSOs, clonedSOs, knob);
                    AddClonedSOs(allSOs, clonedSOs, knob.GetScriptableObjects());
                }
            }

            // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list
            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {             // Clone Nodes, structural content and additional scriptableObjects
                Node node = nodeCanvas.nodes[nodeCnt];
                // Replace node and additional ScriptableObjects
                Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, node);
                clonedNode.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));

                // We're going to restore these from NodeKnobs if desired (!compressed)
                //clonedNode.Inputs = new List<NodeInput> ();
                //clonedNode.Outputs = new List<NodeOutput> ();
                for (int knobCnt = 0; knobCnt < clonedNode.nodeKnobs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeKnob knob = clonedNode.nodeKnobs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.nodeKnobs[knobCnt]);
                    knob.body = clonedNode;
                    // Replace additional scriptableObjects in the NodeKnob
                    knob.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
                }
                for (int knobCnt = 0; knobCnt < clonedNode.Inputs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeInput knob = clonedNode.Inputs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.Inputs[knobCnt]);
                    knob.body = clonedNode;
                }
                for (int knobCnt = 0; knobCnt < clonedNode.Outputs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeOutput knob = clonedNode.Outputs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.Outputs[knobCnt]);
                    knob.body = clonedNode;
                }
            }

            if (editorStates)
            {
                nodeCanvas.editorStates = CreateWorkingCopy(nodeCanvas.editorStates, nodeCanvas);
                foreach (NodeEditorState state in nodeCanvas.editorStates)
                {
                    state.selectedNode = ReplaceSO(allSOs, clonedSOs, state.selectedNode);
                }
            }
            else
            {
                foreach (NodeEditorState state in nodeCanvas.editorStates)
                {
                    state.selectedNode = null;
                }
            }

            return(nodeCanvas);
        }
Esempio n. 3
0
        /// <summary>
        /// Creates a copy of the specified node at pos on the specified canvas, optionally auto-connecting the specified output to a matching input
        /// silent disables any events
        /// </summary>
        public static Node CreateCopy(Node toCopy, Vector2 pos, NodeCanvas hostCanvas, ConnectionPort connectingPort = null, bool silent = false)
        {
            if (toCopy == null || hostCanvas == null)
            {
                throw new ArgumentException();
            }
            if (!NodeCanvasManager.CheckCanvasCompability(toCopy.GetID, hostCanvas.GetType()))
            {
                throw new UnityException("Cannot create Node with ID '" + toCopy.GetID + "' as it is not compatible with the current canvas type (" + hostCanvas.GetType().ToString() + ")!");
            }
            if (!hostCanvas.CanAddNode(toCopy.GetID))
            {
                throw new UnityException("Cannot create Node with ID '" + toCopy.GetID + "' on the current canvas of type (" + hostCanvas.GetType().ToString() + ")!");
            }
            Node node = ScriptableObject.Instantiate(toCopy);

            //Clone static connection ports
            foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(node, true))
            {
                ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(node);
                port = portDecl.portInfo.CreateNew(node);
                portDecl.portField.SetValue(node, port);
            }
            //Clone dynamic connection ports
            for (int i = 0; i < node.dynamicConnectionPorts.Count; ++i)
            {
                node.dynamicConnectionPorts[i]      = ScriptableObject.Instantiate(node.dynamicConnectionPorts[i]);
                node.dynamicConnectionPorts[i].body = node;
                node.dynamicConnectionPorts[i].ClearConnections();
            }
            ConnectionPortManager.UpdateRepresentativePortLists(node);
            //Clone child SOs
            System.Func <ScriptableObject, ScriptableObject> copySOs = (ScriptableObject so) => ScriptableObject.Instantiate(so);;
            node.CopyScriptableObjects(copySOs);
            if (node == null)
            {
                return(null);
            }

            // Init node state
            node.name     = node.Title;
            node.autoSize = node.DefaultSize;
            node.position = pos;
            ConnectionPortManager.UpdateConnectionPorts(node);

            if (connectingPort != null)
            { // Handle auto-connection and link the output to the first compatible input
                for (int i = 0; i < node.connectionPorts.Count; i++)
                {
                    if (node.connectionPorts[i].TryApplyConnection(connectingPort, silent))
                    {
                        break;
                    }
                }
            }

            // Add node to host canvas
            hostCanvas.nodes.Add(node);
            if (!silent)
            { // Callbacks
                hostCanvas.OnNodeChange(connectingPort != null ? connectingPort.body : node);
                NodeEditorCallbacks.IssueOnAddNode(node);
                hostCanvas.Validate();
                NodeEditor.RepaintClients();
            }

            return(node);
        }
Esempio n. 4
0
        public static NodeCanvas CreateWorkingCopy(NodeCanvas nodeCanvas, bool editorStates)
        {
            nodeCanvas.Validate();
            nodeCanvas = Clone(nodeCanvas);
            List <ScriptableObject> allSOs    = new List <ScriptableObject>();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject>();

            for (int i = 0; i < nodeCanvas.nodes.Count; i++)
            {
                Node node = nodeCanvas.nodes[i];
                node.CheckNodeKnobMigration();
                Node node2 = AddClonedSO(allSOs, clonedSOs, node);
                AddClonedSOs(allSOs, clonedSOs, node2.GetScriptableObjects());
                foreach (NodeKnob nodeKnob3 in node2.nodeKnobs)
                {
                    AddClonedSO(allSOs, clonedSOs, nodeKnob3);
                    AddClonedSOs(allSOs, clonedSOs, nodeKnob3.GetScriptableObjects());
                }
            }
            for (int j = 0; j < nodeCanvas.nodes.Count; j++)
            {
                Node initialSO = nodeCanvas.nodes[j];
                Node node3     = ReplaceSO(allSOs, clonedSOs, initialSO);
                nodeCanvas.nodes[j] = node3;
                Node node4 = node3;
                node4.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
                for (int k = 0; k < node4.nodeKnobs.Count; k++)
                {
                    NodeKnob nodeKnob = ReplaceSO(allSOs, clonedSOs, node4.nodeKnobs[k]);
                    node4.nodeKnobs[k] = nodeKnob;
                    NodeKnob nodeKnob2 = nodeKnob;
                    nodeKnob2.body = node4;
                    nodeKnob2.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
                }
                for (int l = 0; l < node4.Inputs.Count; l++)
                {
                    NodeInput nodeInput = ReplaceSO(allSOs, clonedSOs, node4.Inputs[l]);
                    node4.Inputs[l] = nodeInput;
                    NodeInput nodeInput2 = nodeInput;
                    nodeInput2.body = node4;
                }
                for (int m = 0; m < node4.Outputs.Count; m++)
                {
                    NodeOutput nodeOutput = ReplaceSO(allSOs, clonedSOs, node4.Outputs[m]);
                    node4.Outputs[m] = nodeOutput;
                    NodeOutput nodeOutput2 = nodeOutput;
                    nodeOutput2.body = node4;
                }
            }
            if (editorStates)
            {
                nodeCanvas.editorStates = CreateWorkingCopy(nodeCanvas.editorStates, nodeCanvas);
                NodeEditorState[] editorStates2 = nodeCanvas.editorStates;
                foreach (NodeEditorState nodeEditorState in editorStates2)
                {
                    nodeEditorState.selectedNode = ReplaceSO(allSOs, clonedSOs, nodeEditorState.selectedNode);
                }
            }
            else
            {
                NodeEditorState[] editorStates3 = nodeCanvas.editorStates;
                foreach (NodeEditorState nodeEditorState2 in editorStates3)
                {
                    nodeEditorState2.selectedNode = null;
                }
            }
            return(nodeCanvas);
        }
        /// <summary>
        /// Creates a working copy of the specified nodeCanvas, and optionally also of it's associated editorStates.
        /// This breaks the link of this object to any stored assets and references. That means, that all changes to this object will have to be explicitly saved.
        /// </summary>
        public static NodeCanvas CreateWorkingCopy(NodeCanvas nodeCanvas, bool editorStates)
        {
            nodeCanvas.Validate(true);

            // Lists holding initial and cloned version of each ScriptableObject for later replacement of references
            List <ScriptableObject> allSOs    = new List <ScriptableObject> ();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject> ();

            System.Func <ScriptableObject, ScriptableObject> copySOs = (ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so);

            // Clone and enter the canvas object and it's referenced SOs
            nodeCanvas = AddClonedSO(allSOs, clonedSOs, nodeCanvas);
            AddClonedSOs(allSOs, clonedSOs, nodeCanvas.GetScriptableObjects());

            // Iterate over the core ScriptableObjects in the canvas and clone them
            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {
                Node node = nodeCanvas.nodes[nodeCnt];
                node.CheckNodeKnobMigration();

                // Clone Node and additional scriptableObjects
                Node clonedNode = AddClonedSO(allSOs, clonedSOs, node);
                AddClonedSOs(allSOs, clonedSOs, clonedNode.GetScriptableObjects());

                // Clone NodeKnobs
                foreach (NodeKnob knob in clonedNode.nodeKnobs)
                {
                    AddClonedSO(allSOs, clonedSOs, knob);
                }
            }

            // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list
            nodeCanvas.CopyScriptableObjects(copySOs);

            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {             // Replace SOs in all Nodes
                Node node = nodeCanvas.nodes[nodeCnt];

                // Replace node and additional ScriptableObjects with their copies
                Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, node);
                clonedNode.CopyScriptableObjects(copySOs);

                // Replace NodeKnobs and restore Inputs/Outputs by NodeKnob type
                clonedNode.Inputs  = new List <NodeInput> ();
                clonedNode.Outputs = new List <NodeOutput> ();
                for (int knobCnt = 0; knobCnt < clonedNode.nodeKnobs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeKnob knob = clonedNode.nodeKnobs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.nodeKnobs[knobCnt]);
                    knob.body = clonedNode;
                    knob.CopyScriptableObjects(copySOs);
                    // Add inputs/outputs to their lists again
                    if (knob is NodeInput)
                    {
                        clonedNode.Inputs.Add(knob as NodeInput);
                    }
                    else if (knob is NodeOutput)
                    {
                        clonedNode.Outputs.Add(knob as NodeOutput);
                    }
                }
            }

            if (editorStates)             // Clone the editorStates
            {
                nodeCanvas.editorStates = CreateWorkingCopy(nodeCanvas.editorStates, nodeCanvas);
            }
            // Fix references in editorStates to Nodes in the canvas
            foreach (NodeEditorState state in nodeCanvas.editorStates)
            {
                state.selectedNode = ReplaceSO(allSOs, clonedSOs, state.selectedNode);
            }

            return(nodeCanvas);
        }
Esempio n. 6
0
        /// <summary>
        /// CreateWorkingCopy a working copy of the canvas and each editorState. This will break the link to the canvas asset and thus all changes made to the working copy have to be explicitly saved.
        /// Check compressed if the copy is not intended for useage but for storage, this will leave the Inputs and Outputs list of Node empty
        /// </summary>
        public static void CreateWorkingCopy(ref NodeCanvas nodeCanvas, NodeEditorState[] editorStates, bool compressed)
        {
            nodeCanvas = Clone(nodeCanvas);

            // Take each SO, make a clone of it and store both versions in the respective list
            // This will only iterate over the 'source instances'
            List <ScriptableObject> allSOs    = new List <ScriptableObject> ();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject> ();

            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {
                Node node = nodeCanvas.nodes[nodeCnt];
                node.CheckNodeKnobMigration();
                Node clonedNode = (Node)AddClonedSO(allSOs, clonedSOs, node);
                for (int knobCnt = 0; knobCnt < clonedNode.nodeKnobs.Count; knobCnt++)
                {                 // Clone NodeKnobs
//					Debug.Log ("Cloned " + knobCnt + " " + (clonedNode.nodeKnobs[knobCnt] == null? "null" : clonedNode.nodeKnobs[knobCnt].name) + " on node " + node.name);
                    AddClonedSO(allSOs, clonedSOs, clonedNode.nodeKnobs[knobCnt]);
                    // Clone additional scriptableObjects
                    ScriptableObject[] additionalKnobSOs = clonedNode.nodeKnobs[knobCnt].GetScriptableObjects();
                    foreach (ScriptableObject so in additionalKnobSOs)
                    {
                        AddClonedSO(allSOs, clonedSOs, so);
                    }
                }
                for (int transCnt = 0; transCnt < clonedNode.transitions.Count; transCnt++)
                {                 // Clone Transitions
                    Transition trans = clonedNode.transitions[transCnt];
                    if (trans.startNode == node)
                    {
                        AddClonedSO(allSOs, clonedSOs, trans);
//						Debug.Log ("Did copy Transition " + trans.name + " because its Node " + clonedNode.name + " is the start node!");
                    }
                    else
                    {
//						Debug.Log ("Did NOT copy Transition " + trans.name + " because its Node " + clonedNode.name + " is NOT the start node (" + trans.startNode.name + ")!");
                        clonedNode.transitions.RemoveAt(transCnt);
                        transCnt--;
                    }
                }
                // Clone additional scriptableObjects
                ScriptableObject[] additionalNodeSOs = clonedNode.GetScriptableObjects();
                foreach (ScriptableObject so in additionalNodeSOs)
                {
                    AddClonedSO(allSOs, clonedSOs, so);
                }
            }

            // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list

            nodeCanvas.currentNode       = ReplaceSO(allSOs, clonedSOs, nodeCanvas.currentNode);
            nodeCanvas.currentTransition = ReplaceSO(allSOs, clonedSOs, nodeCanvas.currentTransition);
            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {             // Clone Nodes, structural content and additional scriptableObjects
                Node node       = nodeCanvas.nodes[nodeCnt];
                Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, node);
                // We're going to restore these from NodeKnobs if desired (!compressed)
                clonedNode.Inputs  = new List <NodeInput> ();
                clonedNode.Outputs = new List <NodeOutput> ();
                for (int knobCnt = 0; knobCnt < clonedNode.nodeKnobs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeKnob knob = clonedNode.nodeKnobs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.nodeKnobs[knobCnt]);
                    knob.body = clonedNode;
                    // Replace additional scriptableObjects in the NodeKnob
                    knob.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
                    if (!compressed)
                    {                     // Add NodeInputs and NodeOutputs to the apropriate lists in Node if desired (!compressed)
                        if (knob is NodeInput)
                        {
                            clonedNode.Inputs.Add(knob as NodeInput);
                        }
                        else if (knob is NodeOutput)
                        {
                            clonedNode.Outputs.Add(knob as NodeOutput);
                        }
                    }
                }
                for (int transCnt = 0; transCnt < clonedNode.transitions.Count; transCnt++)
                {                 // Clone transitions
                    Transition trans = clonedNode.transitions[transCnt];
                    if (trans.startNode != node)
                    {
                        continue;
                    }
                    trans = clonedNode.transitions[transCnt] = ReplaceSO(allSOs, clonedSOs, trans);
                    if (trans == null)
                    {
                        Debug.LogError("Could not copy transition number " + transCnt + " of Node " + clonedNode.name + "!");
                        continue;
                    }

//					Debug.Log ("Did replace contents of Transition " + trans.name + " because its Node " + clonedNode.name + " is the start node!");
                    trans.startNode = ReplaceSO(allSOs, clonedSOs, trans.startNode);
                    trans.endNode   = ReplaceSO(allSOs, clonedSOs, trans.endNode);

                    if (!compressed)
                    {
                        trans.endNode.transitions.Add(trans);
                    }
                }
                // Replace additional scriptableObjects in the Node
                node.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
            }

            // Also create working copies for specified editorStates, if any
            // Needs to be in the same function as the EditorState references nodes from the NodeCanvas
            if (editorStates != null)
            {
                for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++)
                {
                    if (editorStates[stateCnt] == null)
                    {
                        continue;
                    }

                    NodeEditorState state = editorStates[stateCnt] = Clone(editorStates[stateCnt]);
                    state.canvas       = nodeCanvas;
                    state.focusedNode  = null;
                    state.selectedNode = state.selectedNode != null?ReplaceSO(allSOs, clonedSOs, state.selectedNode) : null;

                    state.makeTransition = null;
                    state.connectOutput  = null;
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// CreateWorkingCopy a working copy of the canvas and each editorState. This will break the link to the canvas asset and thus all changes made to the working copy have to be explicitly saved.
        /// Check compressed if the copy is not intended for useage but for storage, this will leave the Inputs and Outputs list of Node empty
        /// </summary>
        public static void CreateWorkingCopy(ref NodeCanvas nodeCanvas, NodeEditorState[] editorStates, bool compressed)
        {
            nodeCanvas = Clone(nodeCanvas);

            // Take each SO, make a clone of it and store both versions in the respective list
            // This will only iterate over the 'source instances'
            List <ScriptableObject> allSOs    = new List <ScriptableObject> ();
            List <ScriptableObject> clonedSOs = new List <ScriptableObject> ();

            foreach (Node node in nodeCanvas.nodes)
            {
                node.CheckNodeKnobMigration();
                Node clonedNode = AddClonedSO(allSOs, clonedSOs, node);
                foreach (NodeKnob knob in clonedNode.nodeKnobs)
                {                 // Clone NodeKnobs
                    NodeKnob clonedKnob = AddClonedSO(allSOs, clonedSOs, knob);
                    // Clone additional scriptableObjects
                    ScriptableObject[] additionalKnobSOs = clonedKnob.GetScriptableObjects();
                    foreach (ScriptableObject so in additionalKnobSOs)
                    {
                        AddClonedSO(allSOs, clonedSOs, so);
                    }
                }
                // Clone additional scriptableObjects
                ScriptableObject[] additionalNodeSOs = clonedNode.GetScriptableObjects();
                foreach (ScriptableObject so in additionalNodeSOs)
                {
                    AddClonedSO(allSOs, clonedSOs, so);
                }
            }

            // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list

            for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++)
            {             // Clone Nodes, structural content and additional scriptableObjects
                Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, nodeCanvas.nodes[nodeCnt]);
                // We're going to restore these from NodeKnobs if desired (!compressed)
                clonedNode.Inputs  = new List <NodeInput> ();
                clonedNode.Outputs = new List <NodeOutput> ();
                for (int knobCnt = 0; knobCnt < clonedNode.nodeKnobs.Count; knobCnt++)
                {                 // Clone generic NodeKnobs
                    NodeKnob clonedKnob = clonedNode.nodeKnobs[knobCnt] = ReplaceSO(allSOs, clonedSOs, clonedNode.nodeKnobs[knobCnt]);
                    clonedKnob.body = clonedNode;
                    if (!compressed)
                    {                     // Add NodeInputs and NodeOutputs to the apropriate lists in Node if desired (!compressed)
                        if (clonedKnob is NodeInput)
                        {
                            clonedNode.Inputs.Add(clonedKnob as NodeInput);
                        }
                        else if (clonedKnob is NodeOutput)
                        {
                            clonedNode.Outputs.Add(clonedKnob as NodeOutput);
                        }
                    }
                    // Replace additional scriptableObjects in the NodeKnob
                    clonedKnob.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
                }
                // Replace additional scriptableObjects in the Node
                clonedNode.CopyScriptableObjects((ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so));
            }

            // Also create working copies for specified editorStates, if any
            // Needs to be in the same function as the EditorState references nodes from the NodeCanvas
            if (editorStates != null)
            {
                for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++)
                {
                    if (editorStates[stateCnt] == null)
                    {
                        continue;
                    }

                    NodeEditorState clonedState = editorStates[stateCnt] = Clone(editorStates[stateCnt]);
                    clonedState.canvas       = nodeCanvas;
                    clonedState.focusedNode  = null;
                    clonedState.selectedNode = clonedState.selectedNode != null?ReplaceSO(allSOs, clonedSOs, clonedState.selectedNode) : null;

                    clonedState.makeTransition = null;
                    clonedState.connectOutput  = null;
                }
            }
        }