///Check if the canvas is close. public static int TryDialogTree(NodeEditorFramework.NodeCanvas canvas) { Node processing = (canvas as DialogNodeEditor.ConversationCanvas).convStartNode; // TryRecursive(processing, true); Debug.Log("Converter:: TryDialogTree:: Try Result: " + tryNodeCount + " nodes," + tryOutputCount + "Outputs"); return(tryNodeCount); }
public static Conversation ConvertToNewConv(NodeEditorFramework.NodeCanvas canvas) { Conversation conv = Conversation.Create(canvas as DialogNodeEditor.ConversationCanvas); Node processing = (canvas as DialogNodeEditor.ConversationCanvas).convStartNode; ConversationStartNode currentRef = conv.startNode; // Recursive(processing, currentRef, conv); return(conv); }
public static void IssueOnSaveCanvas (NodeCanvas canvas) { if (OnSaveCanvas != null) OnSaveCanvas.Invoke (canvas); for (int cnt = 0; cnt < receiverCount; cnt++) { if (callbackReceiver [cnt] == null) callbackReceiver.RemoveAt (cnt--); else callbackReceiver [cnt].OnSaveCanvas (canvas) ; } }
public static void Save(string path, NodeEditorFramework.NodeCanvas canvas) { Conversation tree = ConvertToNewConv(canvas); AssetDatabase.CreateAsset(tree, path); foreach (BaseDialogNode n in tree.nodes) { AddSubAsset(n, tree); } Debug.Log("Converter:: Save:: Created a new dialog tree."); AssetDatabase.SaveAssets(); }
/// <summary> /// Begins to transition the passed nodeCanvas from beginNode /// </summary> public static void BeginTransitioning(NodeCanvas nodeCanvas, Node beginNode) { if (!nodeCanvas.nodes.Contains (beginNode)) throw new UnityException ("Node to begin transitioning from has to be associated with the passed NodeEditorState!"); nodeCanvas.currentNode = beginNode; nodeCanvas.currentTransition = null; if (!transitioningNodeCanvases.Contains (nodeCanvas)) transitioningNodeCanvases.Add (nodeCanvas); RepaintClients (); // Debug.Log ("Beginning transitioning " + nodeCanvas.name + " from Node " + beginNode.name); nodeCanvas.currentNode.OnEnter (null); #if UNITY_EDITOR NEUpdate -= UpdateTransitions; NEUpdate += UpdateTransitions; #endif }
/// <summary> /// Returns the editorState with the specified name in canvas. If not found it will create a new one with that name. /// </summary> public static NodeEditorState ExtractEditorState(NodeCanvas canvas, string stateName) { NodeEditorState state = null; if (canvas.editorStates.Length > 0) { // Search for the editorState state = canvas.editorStates.First((NodeEditorState s) => s != null && s.name == stateName); } if (state == null) { // Create editorState state = ScriptableObject.CreateInstance <NodeEditorState> (); state.canvas = canvas; // Insert into list int index = canvas.editorStates.Length; System.Array.Resize(ref canvas.editorStates, index + 1); canvas.editorStates[index] = state; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(canvas); #endif } state.name = stateName; return(state); }
/// <summary> /// Returns the node at the position in specified canvas space. /// </summary> public static Node NodeAtPosition(NodeEditorState editorState, NodeCanvas nodeCanvas, Vector2 pos) { if (!editorState.canvasRect.Contains(pos)) { return(null); } for (int nodeCnt = nodeCanvas.nodes.Count - 1; nodeCnt >= 0; nodeCnt--) { // Check from top to bottom because of the render order Node node = nodeCanvas.nodes [nodeCnt]; if (CanvasGUIToScreenRect(node.rect).Contains(pos)) // Node Body { return(node); } foreach (NodeKnob knob in node.nodeKnobs) { // Any edge control if (knob.GetScreenKnob().Contains(pos)) { return(node); } } } return(null); }
public static void SaveSceneNodeCanvas(string saveName, ref NodeCanvas nodeCanvas, bool createWorkingCopy) { if (string.IsNullOrEmpty(saveName)) { Debug.LogError("Cannot save Canvas to scene: No save name specified!"); } else { nodeCanvas.livesInScene = true; nodeCanvas.name = saveName; NodeCanvasSceneSave nodeCanvasSceneSave = FindSceneSave(saveName); if ((UnityEngine.Object)nodeCanvasSceneSave == (UnityEngine.Object)null) { nodeCanvasSceneSave = sceneSaveHolder.AddComponent <NodeCanvasSceneSave>(); } nodeCanvasSceneSave.savedNodeCanvas = nodeCanvas; if (createWorkingCopy) { nodeCanvasSceneSave.savedNodeCanvas = CreateWorkingCopy(nodeCanvasSceneSave.savedNodeCanvas, true); Compress(ref nodeCanvasSceneSave.savedNodeCanvas); } } }
public static NodeCanvas LoadSceneNodeCanvas(string saveName, bool createWorkingCopy) { if (string.IsNullOrEmpty(saveName)) { Debug.LogError("Cannot load Canvas from scene: No save name specified!"); return(null); } NodeCanvasSceneSave nodeCanvasSceneSave = FindSceneSave(saveName); if ((UnityEngine.Object)nodeCanvasSceneSave == (UnityEngine.Object)null) { return(null); } NodeCanvas nodeCanvas = nodeCanvasSceneSave.savedNodeCanvas; nodeCanvas.livesInScene = true; if (createWorkingCopy) { nodeCanvas = CreateWorkingCopy(nodeCanvas, true); } Uncompress(ref nodeCanvas); return(nodeCanvas); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas(string path) { // Else it will be stuck forever NodeEditor.StopTransitioning(mainNodeCanvas); // Load the NodeCanvas mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(path, true); if (mainNodeCanvas == null) { Debug.Log("Error loading NodeCanvas from '" + path + "'!"); NewNodeCanvas(); return; } // Load the associated MainEditorState List <NodeEditorState> editorStates = NodeEditorSaveManager.LoadEditorStates(path, true); if (editorStates.Count == 0) { mainEditorState = ScriptableObject.CreateInstance <NodeEditorState> (); Debug.LogError("The save file '" + path + "' did not contain an associated NodeEditorState!"); } else { mainEditorState = editorStates.Find(x => x.name == "MainEditorState"); if (mainEditorState == null) { mainEditorState = editorStates[0]; } } mainEditorState.canvas = mainNodeCanvas; openedCanvasPath = path; NodeEditor.RecalculateAll(mainNodeCanvas); SaveCache(); Repaint(); }
/// <summary> /// Loads the NodeCanvas from the asset file at path and optionally creates a working copy of it before returning /// </summary> public static NodeCanvas LoadNodeCanvas(string path, bool createWorkingCopy = false) { #if !UNITY_EDITOR throw new System.NotImplementedException(); #else if (string.IsNullOrEmpty(path)) { throw new System.ArgumentNullException("Cannot load Canvas: No path specified!"); } path = ResourceManager.PreparePath(path); // Load only the NodeCanvas from the save file NodeCanvas nodeCanvas = ResourceManager.LoadResource <NodeCanvas> (path); if (nodeCanvas == null) { throw new UnityException("Cannot load NodeCanvas: The file at the specified path '" + path + "' is no valid save file as it does not contain a NodeCanvas!"); } if (!Application.isPlaying && (nodeCanvas.editorStates == null || nodeCanvas.editorStates.Length == 0)) { // Try to load any contained editorStates, as the canvas did not reference any nodeCanvas.editorStates = ResourceManager.LoadResources <NodeEditorState> (path); } // Set the path as the new source of the canvas nodeCanvas.UpdateSource(path); // Postprocess the loaded canvas nodeCanvas.Validate(); if (createWorkingCopy) { nodeCanvas = CreateWorkingCopy(nodeCanvas); } NodeEditorCallbacks.IssueOnLoadCanvas(nodeCanvas); return(nodeCanvas); #endif }
/// <summary> /// Stops the transitioning process of the passed nodeCanvas /// </summary> public static void StopTransitioning(NodeCanvas nodeCanvas) { if (nodeCanvas == null) { return; } if (transitioningNodeCanvases.Contains(nodeCanvas)) { transitioningNodeCanvases.Remove(nodeCanvas); } #if UNITY_EDITOR if (transitioningNodeCanvases.Count == 0) { NEUpdate -= UpdateTransitions; } #endif // Debug.Log ("Stopped transitioning " + nodeCanvas.name + (nodeCanvas.currentNode != null? (" at Node " + nodeCanvas.currentNode.name) : "")); if (nodeCanvas.currentTransition != null) { nodeCanvas.currentTransition.stopTransition(); nodeCanvas.currentTransition = null; } RepaintClients(); }
/// <summary> /// Begins to transition the passed nodeCanvas from beginNode /// </summary> public static void BeginTransitioning(NodeCanvas nodeCanvas, Node beginNode) { if (!nodeCanvas.nodes.Contains(beginNode)) { throw new UnityException("Node to begin transitioning from has to be associated with the passed NodeEditorState!"); } nodeCanvas.currentNode = beginNode; nodeCanvas.currentTransition = null; if (!transitioningNodeCanvases.Contains(nodeCanvas)) { transitioningNodeCanvases.Add(nodeCanvas); } RepaintClients(); // Debug.Log ("Beginning transitioning " + nodeCanvas.name + " from Node " + beginNode.name); nodeCanvas.currentNode.OnEnter(null); #if UNITY_EDITOR NEUpdate -= UpdateTransitions; NEUpdate += UpdateTransitions; #endif }
/// <summary> /// Saves the nodeCanvas in the current scene under the specified name along with the specified editorStates or, if specified, their working copies /// If also stored as an asset, it will loose the reference to the asset first /// </summary> public static void SaveSceneNodeCanvas(string saveName, ref NodeCanvas nodeCanvas, bool createWorkingCopy) { if (string.IsNullOrEmpty(saveName)) { Debug.LogError("Cannot save Canvas to scene: No save name specified!"); return; } #if UNITY_EDITOR // Make sure the canvas has no reference to an asset if (!createWorkingCopy && UnityEditor.AssetDatabase.Contains(nodeCanvas)) { Debug.LogWarning("Forced to create working copy of '" + saveName + "' when saving to scene because it already exists as an asset!"); nodeCanvas = CreateWorkingCopy(nodeCanvas, true); } #endif nodeCanvas.livesInScene = true; nodeCanvas.name = saveName; // Get the saveHolder and the find the existing stored save or create a new one NodeCanvasSceneSave sceneSave = FindSceneSave(saveName); if (sceneSave == null) { sceneSave = sceneSaveHolder.AddComponent <NodeCanvasSceneSave> (); } // Store the canvas and editor states or optionally their working copies sceneSave.savedNodeCanvas = nodeCanvas; if (createWorkingCopy) { sceneSave.savedNodeCanvas = CreateWorkingCopy(sceneSave.savedNodeCanvas, true); Compress(ref sceneSave.savedNodeCanvas); } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(sceneSaveHolder); #endif }
/// <summary> /// Extracts the state with the specified name out of the canvas, takes a random different one and renames it or creates a new one with that name if not found /// </summary> public static NodeEditorState ExtractEditorState(NodeCanvas canvas, string stateName) { NodeEditorState state = null; if (canvas.editorStates.Length > 0) { state = canvas.editorStates.First((NodeEditorState s) => s.name == stateName); if (state == null) { state = canvas.editorStates[0]; } } if (state == null) { state = ScriptableObject.CreateInstance <NodeEditorState> (); state.canvas = canvas; canvas.editorStates = new NodeEditorState[] { state }; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(canvas); #endif } state.name = stateName; return(state); }
/// <summary> /// Loads the canvas from the cache save file /// Called whenever a reload was made /// </summary> public void LoadCache() { #if CACHE if (!useCache) { // Simply create a ne canvas NewNodeCanvas(); return; } bool skipLoad = false; if (cacheMemorySODump) { // Check if a memory dump has been found, if so, load that nodeCanvas = ResourceManager.LoadResource <NodeCanvas>(SOMemoryDumpPath); if (nodeCanvas != null) { skipLoad = true; } } // Try to load the NodeCanvas if (!skipLoad && (!File.Exists(lastSessionPath) || (nodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(lastSessionPath, cacheWorkingCopy)) == null) && // Check for asset cache (nodeCanvas = NodeEditorSaveManager.LoadSceneNodeCanvas("lastSession", cacheWorkingCopy)) == null) // Check for scene cache { NewNodeCanvas(); return; } // Fetch the associated MainEditorState editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); UpdateCanvasInfo(); nodeCanvas.Validate(); nodeCanvas.TraverseAll(); NodeEditor.RepaintClients(); #endif }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// 加载一个Canvas /// </summary> public void LoadNodeCanvas(string path) { //如果路径一致,说明是同一个canvas if (NodeEditor.curEditorState != null && NodeEditor.curEditorState.canvas != null && (NodeEditor.curEditorState.canvas.savePath == path || path.Contains(NodeEditor.curEditorState.canvas.savePath)) && ( NodeEditorSaveManager.GetLastCanvasPath() == path || path.Contains(NodeEditorSaveManager.GetLastCanvasPath()))) { this.nodeCanvas = NodeEditor.curEditorState.canvas; return; } //如果不存在路径,则新建一个DefaultCanvas if (!File.Exists(path) || (nodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(path)) == null) { NewNodeCanvas(); return; } editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); nodeCanvas.Validate(); UpdateCanvasInfo(); NodeEditor.RepaintClients(); Debug.Log($"加载{path}成功"); }
public static NodeCanvas LoadNodeCanvas(string path, bool createWorkingCopy) { if (!File.Exists(path)) { throw new UnityException("Cannot Load NodeCanvas: File '" + path + "' deos not exist!"); } NodeCanvas nodeCanvas = ResourceManager.LoadResource <NodeCanvas>(path); if ((UnityEngine.Object)nodeCanvas == (UnityEngine.Object)null) { throw new UnityException("Cannot Load NodeCanvas: The file at the specified path '" + path + "' is no valid save file as it does not contain a NodeCanvas!"); } if (createWorkingCopy) { nodeCanvas = CreateWorkingCopy(nodeCanvas, true); } else { nodeCanvas.Validate(); } Uncompress(ref nodeCanvas); NodeEditorCallbacks.IssueOnLoadCanvas(nodeCanvas); return(nodeCanvas); }
/// <summary> /// Converts the given canvas to the specified type /// </summary> public static NodeCanvas ConvertCanvasType(NodeCanvas canvas, Type newType) { NodeCanvas convertedCanvas = canvas; if (canvas.GetType() != newType && newType.IsSubclassOf(typeof(NodeCanvas))) { canvas.Validate(); canvas = NodeEditorSaveManager.CreateWorkingCopy(canvas); convertedCanvas = NodeCanvas.CreateCanvas(newType); convertedCanvas.nodes = canvas.nodes; convertedCanvas.groups = canvas.groups; convertedCanvas.editorStates = canvas.editorStates; for (int i = 0; i < convertedCanvas.nodes.Count; i++) { if (!CheckCanvasCompability(convertedCanvas.nodes[i].GetID, newType)) { // Check if nodes is even compatible with the canvas, if not delete it convertedCanvas.nodes[i].Delete(); i--; } } convertedCanvas.Validate(); } return(convertedCanvas); }
/// <summary> /// Loads the nodeCanvas stored in the current scene under the specified name and optionally creates a working copy of it before returning /// </summary> public static NodeCanvas LoadSceneNodeCanvas(string saveName, bool createWorkingCopy) { if (string.IsNullOrEmpty(saveName)) { throw new System.ArgumentNullException("Cannot load Canvas from scene: No save name specified!"); } if (saveName.StartsWith("SCENE/")) { saveName = saveName.Substring(6); } // Load SceneSave NodeCanvasSceneSave sceneSave = FindSceneSave(saveName); if (sceneSave == null || sceneSave.savedNodeCanvas == null) { return(null); } // Extract the saved canvas and editorStates NodeCanvas savedCanvas = sceneSave.savedNodeCanvas; // Set the saveName as the new source of the canvas savedCanvas.UpdateSource("SCENE/" + saveName); // Postprocess the loaded canvas savedCanvas.Validate(); if (createWorkingCopy) { savedCanvas = CreateWorkingCopy(savedCanvas); } NodeEditorCallbacks.IssueOnLoadCanvas(savedCanvas); return(savedCanvas); }
public static void Uncompress(ref NodeCanvas nodeCanvas) { for (int i = 0; i < nodeCanvas.nodes.Count; i++) { Node node = nodeCanvas.nodes[i]; if (node.Inputs == null || node.Inputs.Count == 0 || node.Outputs == null || node.Outputs.Count == 0) { node.Inputs = new List <NodeInput>(); node.Outputs = new List <NodeOutput>(); for (int j = 0; j < node.nodeKnobs.Count; j++) { NodeKnob nodeKnob = node.nodeKnobs[j]; if (nodeKnob is NodeInput) { node.Inputs.Add(nodeKnob as NodeInput); } else if (nodeKnob is NodeOutput) { node.Outputs.Add(nodeKnob as NodeOutput); } } } } }
public static NodeEditorState ExtractEditorState(NodeCanvas canvas, string stateName) { NodeEditorState nodeEditorState = null; if (canvas.editorStates.Length > 0) { nodeEditorState = canvas.editorStates.First((NodeEditorState s) => s.name == stateName); if ((UnityEngine.Object)nodeEditorState == (UnityEngine.Object)null) { nodeEditorState = canvas.editorStates[0]; } } if ((UnityEngine.Object)nodeEditorState == (UnityEngine.Object)null) { nodeEditorState = ScriptableObject.CreateInstance <NodeEditorState>(); nodeEditorState.canvas = canvas; canvas.editorStates = new NodeEditorState[1] { nodeEditorState }; } nodeEditorState.name = stateName; return(nodeEditorState); }
/// <summary> /// Loads the NodeCanvas from the asset file at path and optionally creates a working copy of it before returning /// </summary> public static NodeCanvas LoadNodeCanvas(string path, bool createWorkingCopy) { if (string.IsNullOrEmpty(path)) { throw new UnityException("Cannot Load NodeCanvas: No path specified to load the NodeCanvas from!"); } // Fetch all objects in the save file ScriptableObject[] objects = ResourceManager.LoadResources <ScriptableObject> (path); if (objects == null || objects.Length == 0) { throw new UnityException("Cannot Load NodeCanvas: The specified path '" + path + "' does not point to a save file!"); } // Filter out the NodeCanvas out of these objects NodeCanvas nodeCanvas = objects.Single((ScriptableObject obj) => (obj as NodeCanvas) != null) as NodeCanvas; if (nodeCanvas == null) { throw new UnityException("Cannot Load NodeCanvas: The file at the specified path '" + path + "' is no valid save file as it does not contain a NodeCanvas!"); } #if UNITY_EDITOR // Create a working copy of it if (createWorkingCopy) { CreateWorkingCopy(ref nodeCanvas, false); if (nodeCanvas == null) { throw new UnityException("Cannot Load NodeCanvas: Failed to create a working copy for the NodeCanvas at path '" + path + "' during the loading process!"); } } UnityEditor.AssetDatabase.Refresh(); #endif NodeEditorCallbacks.IssueOnLoadCanvas(nodeCanvas); return(nodeCanvas); }
/// <summary> /// Returns the NodeData for the given canvas /// </summary> public static NodeCanvasTypeData GetCanvasTypeData(NodeCanvas canvas) { return(GetCanvasTypeData(canvas.GetType())); }
/// <summary> /// Creates and opens a new empty node canvas /// </summary> public void NewNodeCanvas() { // New NodeCanvas mainNodeCanvas = CreateInstance<NodeCanvas> (); mainNodeCanvas.nodes = new List<Node> (); // New NodeEditorState mainEditorState = CreateInstance<NodeEditorState> (); mainEditorState.canvas = mainNodeCanvas; mainEditorState.name = "MainEditorState"; // Set some properties openedCanvas = "New Canvas"; openedCanvasPath = ""; }
/// <summary> /// Context Click selection. Here you'll need to register your own using a string identifier /// </summary> public static void ContextCallback(object obj) { callbackObject cbObj = obj as callbackObject; curNodeCanvas = cbObj.canvas; curEditorState = cbObj.editor; switch (cbObj.message) { case "deleteNode": if (cbObj.node != null) cbObj.node.Delete (); break; case "duplicateNode": if (cbObj.node != null) { ContextCallback (new callbackObject (cbObj.node.GetID, curNodeCanvas, curEditorState)); Node duplicatedNode = curNodeCanvas.nodes [curNodeCanvas.nodes.Count-1]; curEditorState.activeNode = duplicatedNode; curEditorState.dragNode = true; } break; default: var createPos = ScreenToGUIPos (mousePos); if (cbObj.nodeOutput != null && (curEditorState.connectMousePos - mousePos).sqrMagnitude < 50) createPos = new Vector2(cbObj.nodeOutput.body.rect.xMax+50, cbObj.nodeOutput.body.rect.yMin); foreach (Node node in NodeTypes.nodes.Keys) { if (node.GetID == cbObj.message) { var newNode = node.Create (createPos); newNode.InitBase (); if (cbObj.nodeOutput != null) { // If nodeOutput is defined, link it to the first input of the same type foreach (var input in newNode.Inputs) { if (Node.CanApplyConnection (cbObj.nodeOutput, input)) { // If it can connect (type is equals, it does not cause recursion, ...) Node.ApplyConnection (cbObj.nodeOutput, input); break; } } } } } break; } }
/// <summary> /// Recalculate from every Input Node. /// Usually does not need to be called at all, the smart calculation system is doing the job just fine. /// Option to not recalculate the inputs, when they are already set manually /// </summary> public static void RecalculateAll(NodeCanvas nodeCanvas, bool calculateInputs) { workList = new List<Node> (); foreach (Node node in nodeCanvas.nodes) { if (node.Inputs.Count == 0) { // Add all Inputs if (calculateInputs) { ClearCalculation (node); workList.Add (node); } else { foreach (NodeOutput output in node.Outputs) { for (int conCnt = 0; conCnt < output.connections.Count; conCnt++) { ClearCalculation (output.connections [conCnt].body); workList.Add (output.connections [conCnt].body); } } } } } StartCalculation (); }
/// <summary> /// Returns the node at the position in specified canvas space. /// </summary> public static Node NodeAtPosition (NodeEditorState editorState, NodeCanvas nodeCanvas, Vector2 pos) { if (!editorState.canvasRect.Contains (pos)) return null; for (int nodeCnt = nodeCanvas.nodes.Count-1; nodeCnt >= 0; nodeCnt--) { // Check from top to bottom because of the render order Node node = nodeCanvas.nodes [nodeCnt]; if (CanvasGUIToScreenRect (node.rect).Contains (pos)) // Node Body return node; foreach (NodeKnob knob in node.nodeKnobs) { // Any edge control if (knob.GetScreenKnob ().Contains (pos)) return node; } } return null; }
/// <summary> /// Context Click selection. Here you'll need to register your own using a string identifier /// </summary> public static void ContextCallback(object obj) { callbackObject cbObj = obj as callbackObject; curNodeCanvas = cbObj.canvas; curEditorState = cbObj.editor; switch (cbObj.message) { case "deleteNode": if (curEditorState.focusedNode != null) curEditorState.focusedNode.Delete (); break; case "duplicateNode": if (curEditorState.focusedNode != null) { ContextCallback (new callbackObject (curEditorState.focusedNode.GetID, curNodeCanvas, curEditorState)); Node duplicatedNode = curNodeCanvas.nodes [curNodeCanvas.nodes.Count-1]; curEditorState.focusedNode = duplicatedNode; curEditorState.dragNode = true; curEditorState.makeTransition = null; curEditorState.connectOutput = null; curEditorState.panWindow = false; } break; case "startTransition": if (curEditorState.focusedNode != null) { curEditorState.makeTransition = curEditorState.focusedNode; curEditorState.connectOutput = null; } curEditorState.dragNode = false; curEditorState.panWindow = false; break; default: Vector2 createPos = ScreenToGUIPos (mousePos); Node node = NodeTypes.getDefaultNode (cbObj.message); if (node == null) break; bool acceptTransitions = NodeTypes.nodes [node].transitions; node = node.Create (createPos); node.InitBase (); NodeEditorCallbacks.IssueOnAddNode (node); if (curEditorState.connectOutput != null) { // If nodeOutput is defined, link it to the first input of the same type foreach (NodeInput input in node.Inputs) { if (Node.CanApplyConnection (curEditorState.connectOutput, input)) { // If it can connect (type is equals, it does not cause recursion, ...) Node.ApplyConnection (curEditorState.connectOutput, input); break; } } } else if (acceptTransitions && curEditorState.makeTransition != null) { Node.CreateTransition (curEditorState.makeTransition, node); } curEditorState.makeTransition = null; curEditorState.connectOutput = null; curEditorState.dragNode = false; curEditorState.panWindow = false; break; } }
/// <summary> /// Draws the Node Canvas on the screen in the rect specified by editorState. Has to be called out of any GUI Group or area because of zooming. /// </summary> public static void DrawCanvas(NodeCanvas nodeCanvas, NodeEditorState editorState) { DrawCanvas (nodeCanvas, editorState, new Rect ()); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas (string path) { // Load the NodeCanvas NodeCanvas nodeCanvas = NodeEditor.LoadNodeCanvas (path); if (nodeCanvas == null) { NewNodeCanvas (); return; } mainNodeCanvas = nodeCanvas; // Load the associated MainEditorState List<NodeEditorState> editorStates = NodeEditor.LoadEditorStates (path); mainEditorState = editorStates.Find (x => x.name == "MainEditorState"); if (mainEditorState == null) mainEditorState = CreateInstance<NodeEditorState> (); // Set some editor properties openedCanvasPath = path; NodeEditor.RecalculateAll (mainNodeCanvas); Repaint (); }
/// <summary> /// Saves the current node canvas as a new asset and links optional editorStates with it /// </summary> public static void SaveNodeCanvas(NodeCanvas nodeCanvas, string path, params NodeEditorState[] editorStates) { if (String.IsNullOrEmpty (path)) return; #if UNITY_EDITOR string existingPath = UnityEditor.AssetDatabase.GetAssetPath (nodeCanvas); if (!String.IsNullOrEmpty (existingPath)) { if (existingPath != path) { UnityEditor.AssetDatabase.CopyAsset (existingPath, path); UnityEditor.AssetDatabase.SaveAssets (); UnityEditor.AssetDatabase.Refresh (); } return; } UnityEditor.AssetDatabase.CreateAsset (nodeCanvas, path); foreach (NodeEditorState editorState in editorStates) UnityEditor.AssetDatabase.AddObjectToAsset (editorState, nodeCanvas); for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) { // Add every node and every of it's inputs/outputs into the file. // Results in a big mess but there's no other way (like a hierarchy) Node node = nodeCanvas.nodes [nodeCnt]; UnityEditor.AssetDatabase.AddObjectToAsset (node, nodeCanvas); for (int inCnt = 0; inCnt < node.Inputs.Count; inCnt++) UnityEditor.AssetDatabase.AddObjectToAsset (node.Inputs [inCnt], node); for (int outCnt = 0; outCnt < node.Outputs.Count; outCnt++) UnityEditor.AssetDatabase.AddObjectToAsset (node.Outputs [outCnt], node); for (int transCnt = 0; transCnt < node.transitions.Count; transCnt++) UnityEditor.AssetDatabase.AddObjectToAsset (node.transitions [transCnt], node); } UnityEditor.AssetDatabase.SaveAssets (); UnityEditor.AssetDatabase.Refresh (); #else // TODO: Node Editor: Need to implement ingame-saving (Resources, AsssetBundles, ... won't work) #endif NodeEditorCallbacks.IssueOnSaveCanvas (nodeCanvas); }
public static void BeginEditingCanvas(NodeCanvas canvas) { NodeEditorState state = canvas.editorStates.Length >= 1? canvas.editorStates[0] : null; BeginEditingCanvas(canvas, state); }
/// <summary> /// Creates and opens a new empty node canvas /// </summary> public void NewNodeCanvas() { // Else it will be stuck forever NodeEditor.StopTransitioning (mainNodeCanvas); // New NodeCanvas mainNodeCanvas = CreateInstance<NodeCanvas> (); mainNodeCanvas.name = "New Canvas"; // New NodeEditorState mainEditorState = CreateInstance<NodeEditorState> (); mainEditorState.canvas = mainNodeCanvas; mainEditorState.name = "MainEditorState"; openedCanvasPath = ""; SaveCache (); }
/// <summary> /// Returns the node at the position in specified canvas space. /// </summary> public static Node NodeAtPosition(NodeEditorState editorState, NodeCanvas nodecanvas, Vector2 pos) { if (!editorState.canvasRect.Contains (pos)) return null; // Check if we clicked inside a window (or knobSize pixels left or right of it at outputs, for faster knob recognition) float KnobSize = (float)NodeEditor.knobSize/editorState.zoom; if (editorState.activeNode != null) { // active Node is drawn ontop, so we check it first Rect NodeRect = new Rect (GUIToScreenRect (editorState.activeNode.rect)); NodeRect = new Rect (NodeRect.x - KnobSize, NodeRect.y, NodeRect.width + KnobSize*2, NodeRect.height); if (NodeRect.Contains (pos)) return editorState.activeNode; } for (int nodeCnt = nodecanvas.nodes.Count-1; nodeCnt >= 0; nodeCnt--) { // checked from top to bottom because of the render order Rect NodeRect = new Rect (GUIToScreenRect (nodecanvas.nodes [nodeCnt].rect)); NodeRect = new Rect (NodeRect.x - KnobSize, NodeRect.y, NodeRect.width + KnobSize*2, NodeRect.height); if (NodeRect.Contains (pos)) return nodecanvas.nodes [nodeCnt]; } return null; }
public NodeEditorMenuCallback (string Message, NodeCanvas nodecanvas, NodeEditorState editorState) { message = Message; canvas = nodecanvas; editor = editorState; contextClickPos = Event.current.mousePosition; }
/// <summary> /// Draws the Node Canvas on the screen in the rect specified by editorState without one-time wrappers like GUISkin and OverlayGUI. Made for nested Canvases (WIP) /// </summary> public static void DrawSubCanvas (NodeCanvas nodeCanvas, NodeEditorState editorState) { if (!editorState.drawing) return; // Store and restore later on in case of this being a nested Canvas NodeCanvas prevNodeCanvas = curNodeCanvas; NodeEditorState prevEditorState = curEditorState; curNodeCanvas = nodeCanvas; curEditorState = editorState; if (Event.current.type == EventType.Repaint) { // Draw Background when Repainting GUI.BeginClip (curEditorState.canvasRect); float width = NodeEditorGUI.Background.width / curEditorState.zoom; float height = NodeEditorGUI.Background.height / curEditorState.zoom; Vector2 offset = curEditorState.zoomPos + curEditorState.panOffset/curEditorState.zoom; offset = new Vector2 (offset.x%width - width, offset.y%height - height); int tileX = Mathf.CeilToInt ((curEditorState.canvasRect.width + (width - offset.x)) / width); int tileY = Mathf.CeilToInt ((curEditorState.canvasRect.height + (height - offset.y)) / height); for (int x = 0; x < tileX; x++) { for (int y = 0; y < tileY; y++) { GUI.DrawTexture (new Rect (offset.x + x*width, offset.y + y*height, width, height), NodeEditorGUI.Background); } } GUI.EndClip (); } // Check the inputs InputEvents (); if (Event.current.type != EventType.Layout) curEditorState.ignoreInput = new List<Rect> (); // We're using a custom scale method, as default one is messing up clipping rect Rect canvasRect = curEditorState.canvasRect; curEditorState.zoomPanAdjust = GUIScaleUtility.BeginScale (ref canvasRect, curEditorState.zoomPos, curEditorState.zoom, false); //GUILayout.Label ("Scaling is Great!"); -> TODO: Test by changing the last bool parameter // ---- BEGIN SCALE ---- // Some features which require drawing (zoomed) if (curEditorState.navigate) { // Draw a curve to the origin/active node for orientation purposes RTEditorGUI.DrawLine ((curEditorState.selectedNode != null? curEditorState.selectedNode.rect.center : curEditorState.panOffset) + curEditorState.zoomPanAdjust, ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom, Color.black, null, 3); RepaintClients (); } if (curEditorState.connectOutput != null) { // Draw the currently drawn connection NodeOutput output = curEditorState.connectOutput; Vector2 startPos = output.GetGUIKnob ().center; Vector2 endPos = ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom; Vector2 endDir = output.GetDirection (); NodeEditorGUI.DrawConnection (startPos, endDir, endPos, NodeEditorGUI.GetSecondConnectionVector (startPos, endPos, endDir), ConnectionTypes.GetTypeData (output.type, true).Color); RepaintClients (); } if (curEditorState.makeTransition != null) { // Draw the currently made transition RTEditorGUI.DrawLine (curEditorState.makeTransition.rect.center + curEditorState.zoomPanAdjust, ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom, Color.grey, null, 3); RepaintClients (); } // Push the active node at the bottom of the draw order. if (Event.current.type == EventType.Layout && curEditorState.selectedNode != null) { curNodeCanvas.nodes.Remove (curEditorState.selectedNode); curNodeCanvas.nodes.Add (curEditorState.selectedNode); } // Draw the transitions and connections. Has to be drawn before nodes as transitions originate from node centers foreach (Node node in curNodeCanvas.nodes) node.DrawConnections (); // Draw the nodes foreach (Node node in curNodeCanvas.nodes) { node.DrawNode (); if (Event.current.type == EventType.Repaint) node.DrawKnobs (); } // ---- END SCALE ---- // End scaling group GUIScaleUtility.EndScale (); // Check events with less priority than node GUI controls LateEvents (); curNodeCanvas = prevNodeCanvas; curEditorState = prevEditorState; }
public virtual void OnLoadCanvas (NodeCanvas canvas) {}
/// <summary> /// Evaluates context callbacks previously registered /// </summary> public static void ContextCallback (object obj) { NodeEditorMenuCallback callback = obj as NodeEditorMenuCallback; if (callback == null) throw new UnityException ("Callback Object passed by context is not of type NodeEditorMenuCallback!"); curNodeCanvas = callback.canvas; curEditorState = callback.editor; switch (callback.message) { case "deleteNode": // Delete request if (curEditorState.focusedNode != null) curEditorState.focusedNode.Delete (); break; case "duplicateNode": // Duplicate request if (curEditorState.focusedNode != null) { ContextCallback (new NodeEditorMenuCallback (curEditorState.focusedNode.GetID, curNodeCanvas, curEditorState)); Node duplicatedNode = curNodeCanvas.nodes [curNodeCanvas.nodes.Count-1]; curEditorState.focusedNode = duplicatedNode; curEditorState.dragNode = true; curEditorState.makeTransition = null; curEditorState.connectOutput = null; curEditorState.panWindow = false; } break; case "startTransition": // Starting a new transition if (curEditorState.focusedNode != null) { curEditorState.makeTransition = curEditorState.focusedNode; curEditorState.connectOutput = null; } curEditorState.dragNode = false; curEditorState.panWindow = false; break; default: // Node creation request Node node = Node.Create (callback.message, ScreenToGUIPos (callback.contextClickPos)); // Handle auto-connection if (curEditorState.connectOutput != null) { // If nodeOutput is defined, link it to the first input of the same type foreach (NodeInput input in node.Inputs) { if (input.CanApplyConnection (curEditorState.connectOutput)) { // If it can connect (type is equals, it does not cause recursion, ...) input.ApplyConnection (curEditorState.connectOutput); break; } } } curEditorState.makeTransition = null; curEditorState.connectOutput = null; curEditorState.dragNode = false; curEditorState.panWindow = false; break; } RepaintClients (); }
/// <summary> /// Recalculate from every Input Node. /// Usually does not need to be called at all, the smart calculation system is doing the job just fine /// </summary> public static void RecalculateAll(NodeCanvas nodeCanvas) { RecalculateAll (nodeCanvas, true); }
/// <summary> /// Recalculate from every Input Node. /// Usually does not need to be called at all, the smart calculation system is doing the job just fine /// </summary> public static void RecalculateAll (NodeCanvas nodeCanvas) { workList = new List<Node> (); foreach (Node node in nodeCanvas.nodes) { if (node.isInput ()) { // Add all Inputs node.ClearCalculation (); workList.Add (node); } } StartCalculation (); }
public virtual void OnLoadCanvas(NodeCanvas canvas) { }
public virtual void OnSaveCanvas (NodeCanvas canvas) {}
/// <summary> /// Stops the transitioning process of the passed nodeCanvas /// </summary> public static void StopTransitioning(NodeCanvas nodeCanvas) { if (nodeCanvas == null) return; if (transitioningNodeCanvases.Contains (nodeCanvas)) transitioningNodeCanvases.Remove (nodeCanvas); #if UNITY_EDITOR if (transitioningNodeCanvases.Count == 0) NEUpdate -= UpdateTransitions; #endif // Debug.Log ("Stopped transitioning " + nodeCanvas.name + (nodeCanvas.currentNode != null? (" at Node " + nodeCanvas.currentNode.name) : "")); if (nodeCanvas.currentTransition != null) { nodeCanvas.currentTransition.stopTransition (); nodeCanvas.currentTransition = null; } RepaintClients (); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas(string path) { // Load the NodeCanvas NodeCanvas nodeCanvas = NodeEditor.LoadNodeCanvas (path); if (nodeCanvas == null) return; mainNodeCanvas = nodeCanvas; // Load the associated MainEditorState List<NodeEditorState> editorStates = NodeEditor.LoadEditorStates (path); mainEditorState = editorStates.Find (x => x.name == "MainEditorState"); if (mainEditorState == null) mainEditorState = CreateInstance<NodeEditorState> (); // Set some editor properties string[] folders = path.Split (new char[] {'/'}, StringSplitOptions.None); openedCanvas = folders [folders.Length-1]; openedCanvasPath = path; NodeEditor.RecalculateAll (mainNodeCanvas); Repaint (); }
/// <summary> /// Creates and opens a new empty node canvas /// </summary> public void NewNodeCanvas() { // New NodeCanvas mainNodeCanvas = CreateInstance<NodeCanvas> (); mainNodeCanvas.name = "New Canvas"; // New NodeEditorState mainEditorState = CreateInstance<NodeEditorState> (); mainEditorState.canvas = mainNodeCanvas; mainEditorState.name = "MainEditorState"; openedCanvasPath = ""; SaveCache (); }
private void LoadCache() { string lastSessionName = EditorPrefs.GetString ("NodeEditorLastSession"); string path = tempSessionPath + "/LastSession.asset"; mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (path, false); if (mainNodeCanvas == null) NewNodeCanvas (); else { mainNodeCanvas.name = lastSessionName; List<NodeEditorState> editorStates = NodeEditorSaveManager.LoadEditorStates (path, false); if (editorStates == null || editorStates.Count == 0 || (mainEditorState = editorStates.Find (x => x.name == "MainEditorState")) == null ) { // New NodeEditorState mainEditorState = CreateInstance<NodeEditorState> (); mainEditorState.canvas = mainNodeCanvas; mainEditorState.name = "MainEditorState"; NodeEditorSaveManager.AddSubAsset (mainEditorState, path); AssetDatabase.SaveAssets (); AssetDatabase.Refresh (); } } }
/// <summary> /// Saves the current node canvas as a new asset and links optional editorStates with it /// </summary> public static void SaveNodeCanvas(NodeCanvas nodeCanvas, string path, params NodeEditorState[] editorStates) { #if UNITY_EDITOR if (String.IsNullOrEmpty (path)) return; string existingPath = UnityEditor.AssetDatabase.GetAssetPath (nodeCanvas); if (!String.IsNullOrEmpty (existingPath)) { if (existingPath != path) { UnityEditor.AssetDatabase.CopyAsset (existingPath, path); UnityEditor.AssetDatabase.SaveAssets (); UnityEditor.AssetDatabase.Refresh (); } return; } UnityEditor.AssetDatabase.CreateAsset (nodeCanvas, path); foreach (NodeEditorState editorState in editorStates) UnityEditor.AssetDatabase.AddObjectToAsset (editorState, nodeCanvas); for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) { // Add every node and every of it's inputs/outputs into the file. // Results in a big mess but there's no other way (like a hierarchy) Node node = nodeCanvas.nodes [nodeCnt]; UnityEditor.AssetDatabase.AddObjectToAsset (node, nodeCanvas); for (int inCnt = 0; inCnt < node.Inputs.Count; inCnt++) UnityEditor.AssetDatabase.AddObjectToAsset (node.Inputs [inCnt], node); for (int outCnt = 0; outCnt < node.Outputs.Count; outCnt++) UnityEditor.AssetDatabase.AddObjectToAsset (node.Outputs [outCnt], node); } UnityEditor.AssetDatabase.SaveAssets (); UnityEditor.AssetDatabase.Refresh (); #endif }
public callbackObject(string Message, NodeCanvas nodecanvas, NodeEditorState editorState, Node Node, NodeOutput NodeOutput) { message = Message; canvas = nodecanvas; editor = editorState; node = Node; nodeOutput = NodeOutput; }
/// <summary> /// Saves the the given NodeCanvas along with the given NodeEditorStates if specified as a new asset, optionally as working copies /// </summary> public static void SaveNodeCanvas(string path, NodeCanvas nodeCanvas, bool createWorkingCopy) { #if !UNITY_EDITOR throw new System.NotImplementedException(); #endif if (string.IsNullOrEmpty(path)) { throw new UnityException("Cannot save NodeCanvas: No spath specified to save the NodeCanvas " + (nodeCanvas != null? nodeCanvas.name : "") + " to!"); } if (nodeCanvas == null) { throw new UnityException("Cannot save NodeCanvas: The specified NodeCanvas that should be saved to path " + path + " is null!"); } if (nodeCanvas.livesInScene) { Debug.LogWarning("Attempting to save scene canvas " + nodeCanvas.name + " to an asset, scene object references will be broken!" + (!createWorkingCopy? " No workingCopy is going to be created, so your scene save is broken, too!" : "")); } #if UNITY_EDITOR if (!createWorkingCopy && UnityEditor.AssetDatabase.Contains(nodeCanvas) && UnityEditor.AssetDatabase.GetAssetPath(nodeCanvas) != path) { Debug.LogError("Trying to create a duplicate save file for '" + nodeCanvas.name + "'! Forcing to create a working copy!"); createWorkingCopy = true; } #endif nodeCanvas.livesInScene = false; nodeCanvas.Validate(); #if UNITY_EDITOR // Preprocess the canvas if (createWorkingCopy) { nodeCanvas = CreateWorkingCopy(nodeCanvas, true); Compress(ref nodeCanvas); } // Write canvas and editorStates UnityEditor.AssetDatabase.CreateAsset(nodeCanvas, path); AddSubAssets(nodeCanvas.editorStates, nodeCanvas); // Write nodes + contents foreach (Node node in nodeCanvas.nodes) { // Write node and additional scriptable objects AddSubAsset(node, nodeCanvas); AddSubAssets(node.GetScriptableObjects(), node); foreach (NodeKnob knob in node.nodeKnobs) { // Write knobs and their additional scriptable objects AddSubAsset(knob, node); AddSubAssets(knob.GetScriptableObjects(), knob); } } nodeCanvas.BeforeSavingCanvas(); UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); #else // TODO: Node Editor: Need to implement ingame-saving (Resources, AsssetBundles, ... won't work) #endif NodeEditorCallbacks.IssueOnSaveCanvas(nodeCanvas); }
/// <summary> /// Draws the Node Canvas on the screen in the rect specified by editorState without one-time wrappers like GUISkin and OverlayGUI. Made for nested Canvases (WIP) /// </summary> private static void DrawSubCanvas(NodeCanvas nodeCanvas, NodeEditorState editorState) { if (!editorState.drawing) { return; } BeginEditingCanvas(nodeCanvas, editorState); if (Event.current.type == EventType.Repaint) { // Draw Background when Repainting // Offset from origin in tile units Vector2 tileOffset = new Vector2(-(curEditorState.zoomPos.x * curEditorState.zoom + curEditorState.panOffset.x) / NodeEditorGUI.Background.width, ((curEditorState.zoomPos.y - curEditorState.canvasRect.height) * curEditorState.zoom + curEditorState.panOffset.y) / NodeEditorGUI.Background.height); // Amount of tiles Vector2 tileAmount = new Vector2(Mathf.Round(curEditorState.canvasRect.width * curEditorState.zoom) / NodeEditorGUI.Background.width, Mathf.Round(curEditorState.canvasRect.height * curEditorState.zoom) / NodeEditorGUI.Background.height); // Draw tiled background GUI.DrawTextureWithTexCoords(curEditorState.canvasRect, NodeEditorGUI.Background, new Rect(tileOffset, tileAmount)); } // Handle input events NodeEditorInputSystem.HandleInputEvents(curEditorState); if (Event.current.type != EventType.Layout) { curEditorState.ignoreInput = new List <Rect> (); } // We're using a custom scale method, as default one is messing up clipping rect Rect canvasRect = curEditorState.canvasRect; curEditorState.zoomPanAdjust = GUIScaleUtility.BeginScale(ref canvasRect, curEditorState.zoomPos, curEditorState.zoom, NodeEditorGUI.isEditorWindow, false); // ---- BEGIN SCALE ---- // Some features which require zoomed drawing: if (curEditorState.navigate) { // Draw a curve to the origin/active node for orientation purposes Vector2 startPos = (curEditorState.selectedNode != null? curEditorState.selectedNode.rect.center : curEditorState.panOffset) + curEditorState.zoomPanAdjust; Vector2 endPos = Event.current.mousePosition; RTEditorGUI.DrawLine(startPos, endPos, Color.green, null, 3); RepaintClients(); } if (curEditorState.connectOutput != null) { // Draw the currently drawn connection NodeOutput output = curEditorState.connectOutput; Vector2 startPos = output.GetGUIKnob().center; Vector2 startDir = output.GetDirection(); Vector2 endPos = Event.current.mousePosition; Vector2 endDir = -startDir; // NodeEditorGUI.GetSecondConnectionVector (startPos, endPos, startDir); <- causes unpleasant jumping when switching polarity NodeEditorGUI.OptimiseBezierDirections(startPos, ref startDir, endPos, ref endDir); NodeEditorGUI.DrawConnection(startPos, startDir, endPos, endDir, output.typeData.Color); RepaintClients(); } // Draw the groups below everything else for (int groupCnt = 0; groupCnt < curNodeCanvas.groups.Count; groupCnt++) { curNodeCanvas.groups [groupCnt].DrawGroup(); } // Push the active node to the top of the draw order. if (Event.current.type == EventType.Layout && curEditorState.selectedNode != null) { curNodeCanvas.nodes.Remove(curEditorState.selectedNode); curNodeCanvas.nodes.Add(curEditorState.selectedNode); } // Draw the transitions and connections. Has to be drawn before nodes as transitions originate from node centers for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) { curNodeCanvas.nodes [nodeCnt].DrawConnections(); } // Draw the nodes for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) { Node node = curNodeCanvas.nodes [nodeCnt]; node.DrawNode(); if (Event.current.type == EventType.Repaint) { node.DrawKnobs(); } } // ---- END SCALE ---- // End scaling group GUIScaleUtility.EndScale(); // Handle input events with less priority than node GUI controls NodeEditorInputSystem.HandleLateInputEvents(curEditorState); EndEditingCanvas(); }
/// <summary> /// Creates a working copy of the specified editorStates. Also remains the link of the canvas to these 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> private static NodeEditorState[] CreateWorkingCopy(NodeEditorState[] editorStates, NodeCanvas associatedNodeCanvas) { if (editorStates == null) { return(new NodeEditorState[0]); } editorStates = (NodeEditorState[])editorStates.Clone(); for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++) { if (editorStates[stateCnt] == null) { continue; } NodeEditorState state = editorStates[stateCnt] = Clone(editorStates[stateCnt]); if (state == null) { Debug.LogError("Failed to create a working copy for an NodeEditorState during the loading process of " + associatedNodeCanvas.name + "!"); continue; } state.canvas = associatedNodeCanvas; } associatedNodeCanvas.editorStates = editorStates; return(editorStates); }
public static void EndEditingCanvas() { curNodeCanvas = editCanvasStack.Pop(); curEditorState = editEditorStateStack.Pop(); }
/// <summary> /// Draws the Node Canvas on the screen in the rect specified by editorState. Has to be called out of any GUI Group or area because of zooming. /// </summary> public static void DrawCanvas(NodeCanvas nodeCanvas, NodeEditorState editorState, Action repaint) { if (!editorState.drawing) return; curNodeCanvas = nodeCanvas; curEditorState = editorState; if (repaint != null) Repaint += repaint; // if (curEditorState.parent != null) // curEditorState.canvasRect.position += curEditorState.parent.zoomPanAdjust; if (Event.current.type == EventType.Repaint) { // Draw Background when Repainting GUI.BeginClip (curEditorState.canvasRect); float width = Background.width / curEditorState.zoom; float height = Background.height / curEditorState.zoom; Vector2 offset = new Vector2 ((curEditorState.panOffset.x / curEditorState.zoom)%width - width, (curEditorState.panOffset.y / curEditorState.zoom)%height - height); int tileX = Mathf.CeilToInt ((curEditorState.canvasRect.width + (width - offset.x)) / width); int tileY = Mathf.CeilToInt ((curEditorState.canvasRect.height + (height - offset.y)) / height); for (int x = 0; x < tileX; x++) { for (int y = 0; y < tileY; y++) { Rect texRect = new Rect (offset.x + x*width, offset.y + y*height, width, height); GUI.DrawTexture (texRect, Background); } } GUI.EndClip (); } // if (curEditorState.parent != null) // curEditorState.canvasRect.position -= curEditorState.parent.zoomPanAdjust - curEditorState.parent.zoomPos; // Fetch all nested nodeEditors and set their canvasRects to be ignored by input, // so it can be handled later, and note it down, as it will be drawn later ontop List<Rect> ignoreInput = new List<Rect> (); for (int childCnt = 0; childCnt < curEditorState.childs.Count; childCnt++) { if (curEditorState.childs [childCnt].drawing) { NodeEditorState nestedEditor = curEditorState.childs [childCnt]; ignoreInput.Add (GUIToScreenRect (nestedEditor.canvasRect)); } } // Check the inputs InputEvents (ignoreInput); // We want to scale our nodes, but as GUI.matrix also scales our widnow's clipping group, // we have to scale it up first to receive a correct one as a result #region Scale Setup // End the default clipping group GUI.EndGroup (); // The Rect of the new clipping group to draw our nodes in Rect ScaledCanvasRect = ScaleRect (curEditorState.canvasRect, curEditorState.zoomPos + curEditorState.canvasRect.position, new Vector2 (curEditorState.zoom, curEditorState.zoom)); ScaledCanvasRect.y += 23; // Header tab height if (curNodeCanvas != NodeEditorWindow.MainNodeCanvas) GUI.DrawTexture (ScaledCanvasRect, Background); // Now continue drawing using the new clipping group GUI.BeginGroup (ScaledCanvasRect); ScaledCanvasRect.position = Vector2.zero; // Adjust because we entered the new group // Because I currently found no way to actually scale to the center of the window rather than (0, 0), // I'm going to cheat and just pan it accordingly to let it appear as if it would scroll to the center // Note, due to that, other controls are still scaled to (0, 0) curEditorState.zoomPanAdjust = ScaledCanvasRect.center - curEditorState.canvasRect.size/2 + curEditorState.zoomPos; // Take a matrix backup to restore back later on Matrix4x4 GUIMatrix = GUI.matrix; // Scale GUI.matrix. After that we have the correct clipping group again. GUIUtility.ScaleAroundPivot (new Vector2 (1/curEditorState.zoom, 1/curEditorState.zoom), curEditorState.zoomPanAdjust); #endregion // Some features which require drawing (zoomed) if (curEditorState.navigate) { // Draw a curve to the origin/active node for orientation purposes DrawLine (curEditorState.activeNode != null? curEditorState.activeNode.rect.center + curEditorState.zoomPanAdjust : curEditorState.panOffset, ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom, Color.black, null, 3); if (Repaint != null) Repaint (); } if (curEditorState.connectOutput != null) { // Draw the currently drawn connection DrawConnection (curEditorState.connectOutput.GetGUIKnob ().center, ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom, ConnectionTypes.GetTypeData (curEditorState.connectOutput.type).col); if (Repaint != null) Repaint (); } if (curEditorState.makeTransition != null) { // Draw the currently made transition DrawLine (curEditorState.makeTransition.rect.center + curEditorState.zoomPanAdjust, ScreenToGUIPos (mousePos) + curEditorState.zoomPos * curEditorState.zoom, Color.grey, null, 3); if (Repaint != null) Repaint (); } // Draw the Node connectors; Seperated because of render order for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) curNodeCanvas.nodes [nodeCnt].DrawConnections (); for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) curNodeCanvas.nodes [nodeCnt].DrawKnobs (); for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) curNodeCanvas.nodes [nodeCnt].DrawTransitions (); // Draw the nodes for (int nodeCnt = 0; nodeCnt < curNodeCanvas.nodes.Count; nodeCnt++) { Node node = curNodeCanvas.nodes [nodeCnt]; //if (node != curEditorState.activeNode) NodeEditor.DrawNode (node); } // Draw the active Node ontop // if (editorState.activeNode != null) // NodeEditor.DrawNode (curEditorState.activeNode); // Draw any node groups out there. Has to be drawn here, because they still need to scale according to their parents, but they mustn't be drawn inside a GUI group for (int editorCnt = 0; editorCnt < curEditorState.childs.Count; editorCnt++) { if (curEditorState.childs [editorCnt].drawing) { NodeEditorState nestedEditor = curEditorState.childs [editorCnt]; nestedEditor.canvasRect.position += curEditorState.zoomPanAdjust; //GUI.DrawTexture (nestedEditor.canvasRect, Background); DrawCanvas (nestedEditor.canvas, nestedEditor, Repaint); nestedEditor.canvasRect.position -= curEditorState.zoomPanAdjust; } } curNodeCanvas = nodeCanvas; curEditorState = editorState; // End scaling group // Set default matrix and clipping group for the rest GUI.matrix = GUIMatrix; GUI.EndGroup (); if (curNodeCanvas.parent == null) { GUI.BeginGroup (new Rect (0, 23, NodeEditorWindow.editor.position.width, NodeEditorWindow.editor.position.height)); } else { Rect parentGroupRect = ScaleRect (curEditorState.parent.canvasRect, curEditorState.parent.zoomPos + curEditorState.parent.canvasRect.position, new Vector2 (curEditorState.parent.zoom, curEditorState.parent.zoom)); parentGroupRect.y += 23; // Header tab height GUI.BeginGroup (parentGroupRect); } // Check events with less priority than node GUI controls LateEvents (ignoreInput); }
/// <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); }
/// <summary> /// Draws the Node Canvas on the screen in the rect specified by editorState /// </summary> public static void DrawCanvas (NodeCanvas nodeCanvas, NodeEditorState editorState) { if (!editorState.drawing) return; checkInit (); NodeEditorGUI.StartNodeGUI (); OverlayGUI.StartOverlayGUI (); DrawSubCanvas (nodeCanvas, editorState); OverlayGUI.EndOverlayGUI (); NodeEditorGUI.EndNodeGUI (); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas (string path) { // Load the NodeCanvas mainNodeCanvas = NodeEditor.LoadNodeCanvas (path); if (mainNodeCanvas == null) { NewNodeCanvas (); return; } // Load the associated MainEditorState List<NodeEditorState> editorStates = NodeEditor.LoadEditorStates (path); if (editorStates.Count == 0) mainEditorState = CreateInstance<NodeEditorState> (); else { mainEditorState = editorStates.Find (x => x.name == "MainEditorState"); if (mainEditorState == null) mainEditorState = editorStates[0]; } mainEditorState.Canvas = mainNodeCanvas; OpenedCanvasPath = path; NodeEditor.RecalculateAll (mainNodeCanvas); //SaveCache (); Repaint (); }
public callbackObject(string Message, NodeCanvas nodecanvas, NodeEditorState editorState) { message = Message; canvas = nodecanvas; editor = editorState; }
public virtual void OnSaveCanvas(NodeCanvas canvas) { }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas(string path) { // Load the NodeCanvas mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (path, true); if (mainNodeCanvas == null) { Debug.Log ("Error loading NodeCanvas from '" + path + "'!"); NewNodeCanvas (); return; } // Load the associated MainEditorState List<NodeEditorState> editorStates = NodeEditorSaveManager.LoadEditorStates (path, true); if (editorStates.Count == 0) { mainEditorState = ScriptableObject.CreateInstance<NodeEditorState> (); Debug.LogError ("The save file '" + path + "' did not contain an associated NodeEditorState!"); } else { mainEditorState = editorStates.Find (x => x.name == "MainEditorState"); if (mainEditorState == null) mainEditorState = editorStates[0]; } mainEditorState.canvas = mainNodeCanvas; openedCanvasPath = path; NodeEditor.RecalculateAll (mainNodeCanvas); SaveCache (); Repaint (); }