diff --git a/Editor/Node_Editor/NodeEditorWindow.cs b/Editor/Node_Editor/NodeEditorWindow.cs index ee35ed8e..b3d2b82d 100644 --- a/Editor/Node_Editor/NodeEditorWindow.cs +++ b/Editor/Node_Editor/NodeEditorWindow.cs @@ -2,6 +2,7 @@ using UnityEditor; using System; using System.IO; +using System.Linq; using System.Collections.Generic; using NodeEditorFramework; @@ -26,6 +27,8 @@ public class NodeEditorWindow : EditorWindow public static string tempSessionPath; // GUI + private string sceneCanvasName = ""; + private Vector2 loadScenePos; public static int sideWindowWidth = 400; private static Texture iconTexture; public Rect sideWindowRect { get { return new Rect (position.width - sideWindowWidth, 0, sideWindowWidth, position.height); } } @@ -34,7 +37,7 @@ public class NodeEditorWindow : EditorWindow #region General /// - /// Opens the Node Editor window + /// Opens the Node Editor window and loads the last session /// [MenuItem ("Window/Node Editor")] public static void OpenNodeEditor () @@ -47,9 +50,6 @@ public static void OpenNodeEditor () _editor.titleContent = new GUIContent ("Node Editor", iconTexture); } - /// - /// Handle opening canvas when double-clicking asset - /// [UnityEditor.Callbacks.OnOpenAsset(1)] private static bool AutoOpenCanvas (int instanceID, int line) { @@ -65,7 +65,7 @@ private static bool AutoOpenCanvas (int instanceID, int line) private void OnEnable () { - _editor = this; // Have to set this after reload + _editor = this; NodeEditor.checkInit (false); NodeEditor.ClientRepaints -= Repaint; @@ -77,11 +77,10 @@ private void OnEnable () EditorLoadingControl.justOpenedNewScene -= NormalReInit; EditorLoadingControl.justOpenedNewScene += NormalReInit; - // Setup Cache tempSessionPath = Path.GetDirectoryName (AssetDatabase.GetAssetPath (MonoScript.FromScriptableObject (this))); - LoadCache (); SetupCacheEvents (); + LoadCache (); } private void NormalReInit () @@ -91,7 +90,12 @@ private void NormalReInit () private void OnDestroy () { + EditorUtility.SetDirty (mainNodeCanvas); + AssetDatabase.SaveAssets (); + AssetDatabase.Refresh (); + NodeEditor.ClientRepaints -= Repaint; + // Clear Cache ClearCacheEvents (); } @@ -118,6 +122,7 @@ private void OnGUI () // Rect canvasRect = GUILayoutUtility.GetRect (600, 600); // if (Event.current.type != EventType.Layout) // mainEditorState.canvasRect = canvasRect; + NodeEditorGUI.StartNodeGUI (); // Perform drawing with error-handling try @@ -134,10 +139,10 @@ private void OnGUI () // Draw Side Window sideWindowWidth = Math.Min (600, Math.Max (200, (int)(position.width / 5))); - NodeEditorGUI.StartNodeGUI (); GUILayout.BeginArea (sideWindowRect, GUI.skin.box); DrawSideWindow (); GUILayout.EndArea (); + NodeEditorGUI.EndNodeGUI (); } @@ -145,6 +150,11 @@ private void DrawSideWindow () { GUILayout.Label (new GUIContent ("Node Editor (" + mainNodeCanvas.name + ")", "Opened Canvas path: " + openedCanvasPath), NodeEditorGUI.nodeLabelBold); + if (GUILayout.Button (new GUIContent ("New Canvas", "Loads an empty Canvas"))) + NewNodeCanvas (); + + GUILayout.Space (6); + if (GUILayout.Button (new GUIContent ("Save Canvas", "Saves the Canvas to a Canvas Save File in the Assets Folder"))) { string path = EditorUtility.SaveFilePanelInProject ("Save Node Canvas", "Node Canvas", "asset", "", NodeEditor.editorPath + "Resources/Saves/"); @@ -161,14 +171,33 @@ private void DrawSideWindow () ShowNotification (new GUIContent ("You should select an asset inside your project folder!")); } else - { - path = path.Replace (Application.dataPath, "Assets"); LoadNodeCanvas (path); - } } - if (GUILayout.Button (new GUIContent ("New Canvas", "Loads an empty Canvas"))) - NewNodeCanvas (); + GUILayout.Space (6); + + GUILayout.BeginHorizontal (); + sceneCanvasName = GUILayout.TextField (sceneCanvasName, GUILayout.ExpandWidth (true)); + if (GUILayout.Button (new GUIContent ("Save to Scene", "Saves the Canvas to the Scene"), GUILayout.ExpandWidth (false))) + { + SaveSceneNodeCanvas (sceneCanvasName); + } + GUILayout.EndHorizontal (); + + if (GUILayout.Button (new GUIContent ("Load from Scene", "Loads the Canvas from the Scene"))) + { + NodeEditorFramework.Utilities.GenericMenu menu = new NodeEditorFramework.Utilities.GenericMenu (); + foreach (string sceneSave in NodeEditorSaveManager.GetSceneSaves ()) + menu.AddItem (new GUIContent (sceneSave), false, LoadSceneCanvasCallback, (object)sceneSave); + menu.Show (loadScenePos); + } + if (Event.current.type == EventType.Repaint) + { + Rect popupPos = GUILayoutUtility.GetLastRect (); + loadScenePos = new Vector2 (popupPos.x+2, popupPos.yMax+2); + } + + GUILayout.Space (6); if (GUILayout.Button (new GUIContent ("Recalculate All", "Initiates complete recalculate. Usually does not need to be triggered manually."))) NodeEditor.RecalculateAll (mainNodeCanvas); @@ -190,47 +219,37 @@ private void DrawSideWindow () private void SetupCacheEvents () { // Load the cache after the NodeEditor was cleared - EditorLoadingControl.lateEnteredPlayMode -= LoadCache; EditorLoadingControl.lateEnteredPlayMode += LoadCache; - - // included in justOpenedNewScene as playmode scene is a new, temporary scene - //EditorLoadingControl.justLeftPlayMode -= LoadCache; - //EditorLoadingControl.justLeftPlayMode += LoadCache; - EditorLoadingControl.justOpenedNewScene -= LoadCache; EditorLoadingControl.justOpenedNewScene += LoadCache; // Add new objects to the cache save file - NodeEditorCallbacks.OnAddNode -= SaveNewNode; NodeEditorCallbacks.OnAddNode += SaveNewNode; - NodeEditorCallbacks.OnAddNodeKnob -= SaveNewNodeKnob; NodeEditorCallbacks.OnAddNodeKnob += SaveNewNodeKnob; } private void ClearCacheEvents () { - // Remove callbacks - EditorLoadingControl.lateEnteredPlayMode -= LoadCache; EditorLoadingControl.justLeftPlayMode -= LoadCache; EditorLoadingControl.justOpenedNewScene -= LoadCache; - NodeEditorCallbacks.OnAddNode -= SaveNewNode; NodeEditorCallbacks.OnAddNodeKnob -= SaveNewNodeKnob; } private string lastSessionPath { get { return tempSessionPath + "/LastSession.asset"; } } - /// - /// Adds the node to the cache save file it belongs to the opened canvas - /// private void SaveNewNode (Node node) { + if (mainNodeCanvas.livesInScene) + return; if (!mainNodeCanvas.nodes.Contains (node)) return; + + CheckCurrentCache (); NodeEditorSaveManager.AddSubAsset (node, lastSessionPath); foreach (ScriptableObject so in node.GetScriptableObjects ()) @@ -243,37 +262,42 @@ private void SaveNewNode (Node node) NodeEditorSaveManager.AddSubAsset (so, knob); } + EditorUtility.SetDirty (mainNodeCanvas); AssetDatabase.SaveAssets (); AssetDatabase.Refresh (); } - /// - /// Adds the nodeKnob to the cache save file it belongs to the opened canvas - /// private void SaveNewNodeKnob (NodeKnob knob) { + if (mainNodeCanvas.livesInScene) + return; if (!mainNodeCanvas.nodes.Contains (knob.body)) return; + + CheckCurrentCache (); NodeEditorSaveManager.AddSubAsset (knob, knob.body); foreach (ScriptableObject so in knob.GetScriptableObjects ()) NodeEditorSaveManager.AddSubAsset (so, knob); + + EditorUtility.SetDirty (mainNodeCanvas); + AssetDatabase.SaveAssets (); + AssetDatabase.Refresh (); } /// /// Creates a new cache save file for the currently loaded canvas - /// Only needs to be called when a new canvas is created or loaded + /// Only called when a new canvas is created or loaded /// private void SaveCache () { - string canvasName = mainNodeCanvas.name; - EditorPrefs.SetString ("NodeEditorLastSession", canvasName); + if (mainNodeCanvas.livesInScene) + return; - NodeEditorSaveManager.SaveNodeCanvas (lastSessionPath, false, mainNodeCanvas, mainEditorState); - mainNodeCanvas.name = canvasName; + mainNodeCanvas.editorStates = new NodeEditorState[] { mainEditorState }; + NodeEditorSaveManager.SaveNodeCanvas (lastSessionPath, mainNodeCanvas, false); - AssetDatabase.SaveAssets (); - AssetDatabase.Refresh (); + CheckCurrentCache (); } /// @@ -282,26 +306,32 @@ private void SaveCache () /// private void LoadCache () { - string lastSessionName = EditorPrefs.GetString ("NodeEditorLastSession"); - mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (lastSessionPath, false); - if (mainNodeCanvas == null) - NewNodeCanvas (); - else + // Try to load the NodeCanvas + if (!File.Exists (lastSessionPath) || (mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (lastSessionPath, false)) == null) { - mainNodeCanvas.name = lastSessionName; - List editorStates = NodeEditorSaveManager.LoadEditorStates (lastSessionPath, false); - if (editorStates == null || editorStates.Count == 0 || (mainEditorState = editorStates.Find (x => x.name == "MainEditorState")) == null ) - { // New NodeEditorState - mainEditorState = CreateInstance (); - mainEditorState.canvas = mainNodeCanvas; - mainEditorState.name = "MainEditorState"; - NodeEditorSaveManager.AddSubAsset (mainEditorState, lastSessionPath); - AssetDatabase.SaveAssets (); - AssetDatabase.Refresh (); - } + NewNodeCanvas (); + return; + } - NodeEditor.RecalculateAll (mainNodeCanvas); + // Fetch the associated MainEditorState + if (mainNodeCanvas.editorStates.Length > 0) + mainEditorState = mainNodeCanvas.editorStates.Length == 1? mainNodeCanvas.editorStates[0] : mainNodeCanvas.editorStates.First ((NodeEditorState state) => state.name == "MainEditorState"); + if (mainEditorState == null) + { + NewEditorState (); + NodeEditorSaveManager.AddSubAsset (mainEditorState, lastSessionPath); } + + CheckCurrentCache (); + + NodeEditor.RecalculateAll (mainNodeCanvas); + Repaint (); + } + + private void CheckCurrentCache () + { + if (AssetDatabase.GetAssetPath (mainNodeCanvas) != lastSessionPath) + throw new UnityException ("Cache system error: Current Canvas is not saved as the temporary cache!"); } // private void DeleteCache () @@ -315,22 +345,50 @@ private void LoadCache () // EditorPrefs.DeleteKey ("NodeEditorLastSession"); // } - private void CheckCurrentCache () + #endregion + + #region Save/Load + + private void LoadSceneCanvasCallback (object save) { - if (AssetDatabase.GetAssetPath (mainNodeCanvas) != lastSessionPath) - throw new UnityException ("Cache system error: Current Canvas is not saved as the temporary cache!"); + LoadSceneNodeCanvas ((string)save); } - #endregion + /// + /// Saves the mainNodeCanvas and it's associated mainEditorState as an asset at path + /// + public void SaveSceneNodeCanvas (string path) + { + mainNodeCanvas.editorStates = new NodeEditorState[] { mainEditorState }; + NodeEditorSaveManager.SaveSceneNodeCanvas (path, ref mainNodeCanvas, true); + Repaint (); + } - #region Save/Load + /// + /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path + /// + public void LoadSceneNodeCanvas (string path) + { + // Try to load the NodeCanvas + if ((mainNodeCanvas = NodeEditorSaveManager.LoadSceneNodeCanvas (path, true)) == null) + { + NewNodeCanvas (); + return; + } + mainEditorState = NodeEditorSaveManager.ExtractEditorState (mainNodeCanvas, "MainEditorState"); + + openedCanvasPath = path; + NodeEditor.RecalculateAll (mainNodeCanvas); + Repaint (); + } /// /// Saves the mainNodeCanvas and it's associated mainEditorState as an asset at path /// public void SaveNodeCanvas (string path) { - NodeEditorSaveManager.SaveNodeCanvas (path, true, mainNodeCanvas, mainEditorState); + mainNodeCanvas.editorStates = new NodeEditorState[] { mainEditorState }; + NodeEditorSaveManager.SaveNodeCanvas (path, mainNodeCanvas, true); Repaint (); } @@ -339,34 +397,13 @@ public void SaveNodeCanvas (string path) /// public void LoadNodeCanvas (string path) { - // Load the NodeCanvas - mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (path, true); - if (mainNodeCanvas == null) + // Try to load the NodeCanvas + if (!File.Exists (path) || (mainNodeCanvas = NodeEditorSaveManager.LoadNodeCanvas (path, true)) == null) { - Debug.Log ("Could not load NodeCanvas from '" + path + "'!"); NewNodeCanvas (); return; } - // Retore or save name - if (mainNodeCanvas.name != "lastSession") - EditorPrefs.SetString ("NodeEditorLastSession", mainNodeCanvas.name); - else - mainNodeCanvas.name = EditorPrefs.GetString ("NodeEditorLastSession"); - - // Load the associated MainEditorState - List editorStates = NodeEditorSaveManager.LoadEditorStates (path, false); - if (editorStates.Count == 0) - { - mainEditorState = ScriptableObject.CreateInstance (); - 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]; - } - NodeEditorSaveManager.CreateWorkingCopy (ref mainEditorState); - mainEditorState.canvas = mainNodeCanvas; + mainEditorState = NodeEditorSaveManager.ExtractEditorState (mainNodeCanvas, "MainEditorState"); openedCanvasPath = path; SaveCache (); @@ -375,21 +412,28 @@ public void LoadNodeCanvas (string path) } /// - /// Creates and opens a new empty node canvas + /// Creates and loads a new NodeCanvas /// public void NewNodeCanvas () { - // New NodeCanvas mainNodeCanvas = CreateInstance (); mainNodeCanvas.name = "New Canvas"; - EditorPrefs.SetString ("NodeEditorLastSession", "New Canvas"); - // New NodeEditorState + //EditorPrefs.SetString ("NodeEditorLastSession", "New Canvas"); + NewEditorState (); + openedCanvasPath = ""; + SaveCache (); + } + + /// + /// Creates a new EditorState for the current NodeCanvas + /// + public void NewEditorState () + { mainEditorState = CreateInstance (); mainEditorState.canvas = mainNodeCanvas; mainEditorState.name = "MainEditorState"; - - openedCanvasPath = ""; - SaveCache (); + mainNodeCanvas.editorStates = new NodeEditorState[] { mainEditorState }; + EditorUtility.SetDirty (mainNodeCanvas); } #endregion diff --git a/Node_Editor/Framework/Node.cs b/Node_Editor/Framework/Node.cs index 261ee111..d1517782 100644 --- a/Node_Editor/Framework/Node.cs +++ b/Node_Editor/Framework/Node.cs @@ -16,11 +16,9 @@ public abstract class Node : ScriptableObject public List nodeKnobs = new List (); // Calculation graph -// [NonSerialized] - [SerializeField, HideInInspector] + [NonSerialized] public List Inputs = new List(); -// [NonSerialized] - [SerializeField, HideInInspector] + [NonSerialized] public List Outputs = new List(); [HideInInspector] [NonSerialized] @@ -541,8 +539,8 @@ public void ClearCalculation () #region Recursive Search Helpers - private List recursiveSearchSurpassed; - private Node startRecursiveSearchNode; // Temporary start node for recursive searches + [NonSerialized] private List recursiveSearchSurpassed; + [NonSerialized] private Node startRecursiveSearchNode; // Temporary start node for recursive searches /// /// Begins the recursive search loop and returns whether this node has already been searched diff --git a/Node_Editor/Framework/NodeCanvas.cs b/Node_Editor/Framework/NodeCanvas.cs index 58593255..c1d5c40e 100644 --- a/Node_Editor/Framework/NodeCanvas.cs +++ b/Node_Editor/Framework/NodeCanvas.cs @@ -1,5 +1,6 @@ using UnityEngine; using System.Collections.Generic; +using System.Linq; using NodeEditorFramework; namespace NodeEditorFramework @@ -8,6 +9,72 @@ public class NodeCanvas : ScriptableObject { // Just contains the nodes and global canvas stuff; an associated NodeEditorState holds the actual state now public List nodes = new List (); + public NodeEditorState[] editorStates = new NodeEditorState[0]; + public bool livesInScene = false; + + /// + /// Will validate this canvas for any broken nodes or references and cleans them. + /// + public void Validate () + { + if (nodes == null) + { + Debug.LogWarning ("NodeCanvas '" + name + "' nodes were erased and set to null! Automatically fixed!"); + nodes = new List (); + } + for (int nodeCnt = 0; nodeCnt < nodes.Count; nodeCnt++) + { + Node node = nodes[nodeCnt]; + if (node == null) + { + Debug.LogWarning ("NodeCanvas '" + name + "' contained broken (null) nodes! Automatically fixed!"); + nodes.RemoveAt (nodeCnt); + nodeCnt--; + continue; + } + for (int knobCnt = 0; knobCnt < node.nodeKnobs.Count; knobCnt++) + { + NodeKnob nodeKnob = node.nodeKnobs[knobCnt]; + if (nodeKnob == null) + { + Debug.LogWarning ("NodeCanvas '" + name + "' Node '" + node.name + "' contained broken (null) NodeKnobs! Automatically fixed!"); + node.nodeKnobs.RemoveAt (knobCnt); + knobCnt--; + continue; + } + + if (nodeKnob is NodeInput) + { + NodeInput input = nodeKnob as NodeInput; + if (input.connection != null && input.connection.body == null) + { // References broken node; Clear connection + input.connection = null; + } +// for (int conCnt = 0; conCnt < (nodeKnob as NodeInput).connection.Count; conCnt++) + } + else if (nodeKnob is NodeOutput) + { + NodeOutput output = nodeKnob as NodeOutput; + for (int conCnt = 0; conCnt < output.connections.Count; conCnt++) + { + NodeInput con = output.connections[conCnt]; + if (con == null || con.body == null) + { // Broken connection; Clear connection + output.connections.RemoveAt (conCnt); + conCnt--; + } + } + } + } + } + + if (editorStates == null) + { + Debug.LogWarning ("NodeCanvas '" + name + "' editorStates were erased! Automatically fixed!"); + editorStates = new NodeEditorState[0]; + } + editorStates = editorStates.Where ((NodeEditorState state) => state != null).ToArray (); + } } } \ No newline at end of file diff --git a/Node_Editor/Framework/NodeCanvasSceneSave.cs b/Node_Editor/Framework/NodeCanvasSceneSave.cs new file mode 100644 index 00000000..41c529c5 --- /dev/null +++ b/Node_Editor/Framework/NodeCanvasSceneSave.cs @@ -0,0 +1,7 @@ +using UnityEngine; +using NodeEditorFramework; + +public class NodeCanvasSceneSave : MonoBehaviour +{ + public NodeCanvas savedNodeCanvas; +} diff --git a/Node_Editor/Framework/NodeCanvasSceneSave.cs.meta b/Node_Editor/Framework/NodeCanvasSceneSave.cs.meta new file mode 100644 index 00000000..60fa6906 --- /dev/null +++ b/Node_Editor/Framework/NodeCanvasSceneSave.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3f1cd8579a6c9ec4b85d9d62cd5ba58b +timeCreated: 1464014559 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Node_Editor/Framework/NodeEditor.cs b/Node_Editor/Framework/NodeEditor.cs index 0c4067ff..5adc327d 100644 --- a/Node_Editor/Framework/NodeEditor.cs +++ b/Node_Editor/Framework/NodeEditor.cs @@ -79,7 +79,7 @@ public static void ReInit (bool GUIFunction) UnityEditor.EditorApplication.update += Update; RepaintClients (); #endif - initiated = true; + initiated = GUIFunction; } /// @@ -125,11 +125,7 @@ public static void DrawCanvas (NodeCanvas nodeCanvas, NodeEditorState editorStat return; checkInit (true); - NodeEditorGUI.StartNodeGUI (); - OverlayGUI.StartOverlayGUI (); DrawSubCanvas (nodeCanvas, editorState); - OverlayGUI.EndOverlayGUI (); - NodeEditorGUI.EndNodeGUI (); } /// diff --git a/Node_Editor/Framework/NodeEditorGUI.cs b/Node_Editor/Framework/NodeEditorGUI.cs index 86549d86..6fa79241 100644 --- a/Node_Editor/Framework/NodeEditorGUI.cs +++ b/Node_Editor/Framework/NodeEditorGUI.cs @@ -74,15 +74,20 @@ public static bool Init (bool GUIFunction) public static void StartNodeGUI () { - defaultSkin = GUI.skin; - if (nodeSkin == null) - Init (true); - GUI.skin = nodeSkin; + if (GUI.skin != defaultSkin) + { + if (nodeSkin == null) + Init (true); + GUI.skin = nodeSkin; + } + OverlayGUI.StartOverlayGUI (); } public static void EndNodeGUI () { - GUI.skin = defaultSkin; + OverlayGUI.EndOverlayGUI (); + if (GUI.skin == defaultSkin) + GUI.skin = defaultSkin; } #region Connection Drawing diff --git a/Node_Editor/Framework/NodeEditorSaveManager.cs b/Node_Editor/Framework/NodeEditorSaveManager.cs index a8ddc28e..b27da5c3 100644 --- a/Node_Editor/Framework/NodeEditorSaveManager.cs +++ b/Node_Editor/Framework/NodeEditorSaveManager.cs @@ -1,4 +1,5 @@ using UnityEngine; +using System.IO; using System.Linq; using System.Collections.Generic; @@ -8,221 +9,297 @@ namespace NodeEditorFramework { /// - /// Manager handling all save and load operations on NodeCanvases and NodeEditorStates of the Node Editor + /// Manager handling all save and load operations on NodeCanvases and NodeEditorStates of the Node Editor, both as assets and in the scene /// public static class NodeEditorSaveManager { - #region Save + #region Scene Saving + + private static GameObject sceneSaveHolder; /// - /// Saves a working copy of the given NodeCanvas as a new asset along with working copies of the given NodeEditorStates + /// Fetches the saveHolder of the current scene if not already found or creates it and stores it into sceneSaveHolder /// - public static void SaveNodeCanvas (string path, NodeCanvas nodeCanvas, params NodeEditorState[] editorStates) + private static void FetchSceneSaveHolder () { - SaveNodeCanvas (path, true, nodeCanvas, editorStates); + if (sceneSaveHolder == null) + { + // TODO: Might need to check here if the object is in the active scene / the system works with multiple scenes + //#if UNITY_5_3 | UNITY_5_3_OR_NEWER + //if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ()) + //#endif + sceneSaveHolder = GameObject.Find ("NodeEditor_SceneSaveHolder"); + if (sceneSaveHolder == null) + sceneSaveHolder = new GameObject ("NodeEditor_SceneSaveHolder"); + sceneSaveHolder.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; + } } /// - /// Saves the the given NodeCanvas or, if specified, a working copy of it, as a new asset along with working copies, if specified, of the given NodeEditorStates + /// Gets all existing stored saves in the current scene and returns their names /// - public static void SaveNodeCanvas (string path, bool createWorkingCopy, NodeCanvas nodeCanvas, params NodeEditorState[] editorStates) + public static string[] GetSceneSaves () + { // Get the saveHolder, find the existing stored saves and return their names + FetchSceneSaveHolder (); + return sceneSaveHolder.GetComponents ().Select (((NodeCanvasSceneSave save) => save.savedNodeCanvas.name)).ToArray (); + } + + /// + /// Finds a scene save in the current scene with specified name or null if it does not exist + /// + private static NodeCanvasSceneSave FindSceneSave (string saveName) { - 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!"); + FetchSceneSaveHolder (); + return sceneSaveHolder.GetComponents ().ToList ().Find ((NodeCanvasSceneSave save) => save.savedNodeCanvas.name == saveName); + } - for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++) + /// + /// 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 + /// + public static void SaveSceneNodeCanvas (string saveName, ref NodeCanvas nodeCanvas, bool createWorkingCopy) + { + if (string.IsNullOrEmpty (saveName)) { - if (editorStates[stateCnt] == null) - Debug.LogError ("A NodeEditorState that should be saved to path " + path + " is null!"); + 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 (); + + // 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 + } + + /// + /// Loads the nodeCanvas and it's editorState stored in the current scene under the specified name and, if specified, creates working copies before returning + /// + 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; } - path = path.Replace (Application.dataPath, "Assets"); + NodeCanvasSceneSave sceneSave = FindSceneSave (saveName); + if (sceneSave == null) // No such save file + return null; + + // Extract the saved canvas and editorStates + NodeCanvas savedCanvas = sceneSave.savedNodeCanvas; + savedCanvas.livesInScene = true; + + // Postprocess the loaded canvas + if (createWorkingCopy) // Create working copies if specified + savedCanvas = CreateWorkingCopy (savedCanvas, true); + Uncompress (ref savedCanvas); - #if UNITY_EDITOR + #if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty (sceneSaveHolder); + #endif + + return savedCanvas; + } + + #endregion + #region Asset Saving + + /// + /// Saves the the given NodeCanvas along with the given NodeEditorStates if specified as a new asset, optionally as working copies + /// + 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) throw new UnityException ("Cannot save NodeCanvas: The specified NodeCanvas '" + nodeCanvas.name + "' lives in the scene and thus cannot be saved as an asset!"); + #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.Validate (); + + #if UNITY_EDITOR + // Preprocess the canvas if (createWorkingCopy) - { // Copy canvas and editorStates - CreateWorkingCopy (ref nodeCanvas, editorStates, true); + { + nodeCanvas = CreateWorkingCopy (nodeCanvas, true); + Compress (ref nodeCanvas); } // Write canvas and editorStates UnityEditor.AssetDatabase.CreateAsset (nodeCanvas, path); - for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++) - { - if (editorStates[stateCnt] != null) - AddSubAsset (editorStates[stateCnt], nodeCanvas); - } + AddSubAssets (nodeCanvas.editorStates, nodeCanvas); // Write nodes + contents foreach (Node node in nodeCanvas.nodes) - { - // Write node and additional scriptable objects + { // Write node and additional scriptable objects AddSubAsset (node, nodeCanvas); - foreach (ScriptableObject so in node.GetScriptableObjects ()) - AddSubAsset (so, node); - // Write knobs and their additional scriptable objects + AddSubAssets (node.GetScriptableObjects (), node); foreach (NodeKnob knob in node.nodeKnobs) - { + { // Write knobs and their additional scriptable objects AddSubAsset (knob, node); - foreach (ScriptableObject so in knob.GetScriptableObjects ()) - AddSubAsset (so, knob); + AddSubAssets (knob.GetScriptableObjects (), knob); } } UnityEditor.AssetDatabase.SaveAssets (); UnityEditor.AssetDatabase.Refresh (); - #else + #else // TODO: Node Editor: Need to implement ingame-saving (Resources, AsssetBundles, ... won't work) - #endif + #endif NodeEditorCallbacks.IssueOnSaveCanvas (nodeCanvas); } - #if UNITY_EDITOR - /// - /// Adds a hidden sub asset to the main asset + /// Loads the NodeCanvas from the asset file at path and optionally creates a working copy of it before returning /// - public static void AddSubAsset (ScriptableObject subAsset, ScriptableObject mainAsset) + public static NodeCanvas LoadNodeCanvas (string path, bool createWorkingCopy) { - UnityEditor.AssetDatabase.AddObjectToAsset (subAsset, mainAsset); - subAsset.hideFlags = HideFlags.HideInHierarchy; - } + if (!File.Exists (path)) throw new UnityException ("Cannot Load NodeCanvas: File '" + path + "' deos not exist!"); - /// - /// Adds a hidden sub asset to the main asset - /// - public static void AddSubAsset (ScriptableObject subAsset, string path) - { - UnityEditor.AssetDatabase.AddObjectToAsset (subAsset, path); - subAsset.hideFlags = HideFlags.HideInHierarchy; - } + // Load only the NodeCanvas from the save file + NodeCanvas nodeCanvas = ResourceManager.LoadResource (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!"); - #endif + #if UNITY_EDITOR + if (!Application.isPlaying && (nodeCanvas.editorStates == null || nodeCanvas.editorStates.Length == 0)) + { // Try to load any contained editorStates, possibly old format that did not references the states in the canvas + nodeCanvas.editorStates = ResourceManager.LoadResources (path); + } + #endif - #endregion + // Postprocess the loaded canvas + if (createWorkingCopy) + nodeCanvas = CreateWorkingCopy (nodeCanvas, true); + else + nodeCanvas.Validate (); + Uncompress (ref nodeCanvas); - #region Load + #if UNITY_EDITOR + UnityEditor.AssetDatabase.Refresh (); + #endif + NodeEditorCallbacks.IssueOnLoadCanvas (nodeCanvas); + return nodeCanvas; + } + + #region Utility + + #if UNITY_EDITOR /// - /// Loads the NodeCanvas from the asset file at path and returns a working copy of it + /// Adds the specified hidden subAssets to the mainAsset /// - public static NodeCanvas LoadNodeCanvas (string path) + public static void AddSubAssets (ScriptableObject[] subAssets, ScriptableObject mainAsset) { - return LoadNodeCanvas (path, true); + foreach (ScriptableObject subAsset in subAssets) + AddSubAsset (subAsset, mainAsset); } /// - /// Loads the NodeCanvas from the asset file at path and optionally creates a working copy of it before returning + /// Adds the specified hidden subAsset to the mainAsset /// - public static NodeCanvas LoadNodeCanvas (string path, bool createWorkingCopy) + public static void AddSubAsset (ScriptableObject subAsset, ScriptableObject mainAsset) { - 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 (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!"); - - // Create a working copy of it - if (createWorkingCopy) + if (subAsset != null && mainAsset != null) { - 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!"); - if (nodeCanvas.nodes == null || nodeCanvas.nodes.Any ((Node node) => node == null)) - throw new UnityException ("Cannot Load NodeCanvas: NodeCanvas " + nodeCanvas.name + " was saved incorrectly!"); + UnityEditor.AssetDatabase.AddObjectToAsset (subAsset, mainAsset); + subAsset.hideFlags = HideFlags.HideInHierarchy; } - #if UNITY_EDITOR - UnityEditor.AssetDatabase.Refresh (); - #endif - NodeEditorCallbacks.IssueOnLoadCanvas (nodeCanvas); - return nodeCanvas; } /// - /// Loads the editorStates found in the nodeCanvas asset file at path and returns a working copy of it + /// Adds the specified hidden subAsset to the mainAsset at path /// - public static List LoadEditorStates (string path) + public static void AddSubAsset (ScriptableObject subAsset, string path) { - return LoadEditorStates (path, true); + if (subAsset != null && !string.IsNullOrEmpty (path)) + { + UnityEditor.AssetDatabase.AddObjectToAsset (subAsset, path); + subAsset.hideFlags = HideFlags.HideInHierarchy; + } } - /// - /// Loads the editorStates found in the nodeCanvas asset file at path and optionally creates a working copy of it before returning - /// When creating the working copy, you need to account for the accompanying canvas stored in the states - /// - public static List LoadEditorStates (string path, bool createWorkingCopy) - { - if (string.IsNullOrEmpty (path)) - throw new UnityException ("Cannot load NodeEditorStates: No path specified to load the EditorStates from!"); - - // Fetch all editorStates in the save file - List editorStates = ResourceManager.LoadResources (path).ToList (); - #if UNITY_EDITOR - if (createWorkingCopy) - { // Create a working copy of the editorStates - for (int cnt = 0; cnt < editorStates.Count; cnt++) - { - NodeEditorState state = editorStates[cnt]; - CreateWorkingCopy (ref state); - editorStates[cnt] = state; - if (state == null) throw new UnityException ("Failed to create a working copy for an NodeEditorState at path '" + path + "' during the loading process!"); - } - } - #endif + #endif - // Perform Event and Database refresh - for (int cnt = 0; cnt < editorStates.Count; cnt++) - NodeEditorCallbacks.IssueOnLoadEditorState (editorStates[cnt]); - #if UNITY_EDITOR - UnityEditor.AssetDatabase.Refresh (); - #endif - return editorStates; - } + #endregion #endregion - #region Working Copy Creation + #region Compression - // - /// Gets a working copy of the editor state. This will break the link to the asset and thus all changes made to the working copy have to be explicitly saved. - /// NOTE: If possible, create the working copy with the associated canvas. Else, you have to manually assign the working copy canvas! + /// + /// Compresses the nodeCanvas, meaning it will remove duplicate references that are only for convenience /// - public static void CreateWorkingCopy (ref NodeEditorState editorState) + public static void Compress (ref NodeCanvas nodeCanvas) { - if (editorState == null) - return; - - editorState = Clone (editorState); - editorState.focusedNode = null; - editorState.selectedNode = null; - editorState.connectOutput = null; + for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) + { + Node node = nodeCanvas.nodes[nodeCnt]; + node.Inputs = new List (); + node.Outputs = new List (); + } } /// - /// Creates a working copy of the canvas. 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 + /// Uncompresses the nodeCanvas, meaning it will restore duplicate references for convenience /// - public static void CreateWorkingCopy (ref NodeCanvas nodeCanvas, bool compressed) + public static void Uncompress (ref NodeCanvas nodeCanvas) { - CreateWorkingCopy (ref nodeCanvas, null, compressed); + for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) + { + Node node = nodeCanvas.nodes[nodeCnt]; + node.Inputs = new List (); + node.Outputs = new List (); + for (int knobCnt = 0; knobCnt < node.nodeKnobs.Count; knobCnt++) + { + NodeKnob knob = node.nodeKnobs[knobCnt]; + if (knob is NodeInput) + node.Inputs.Add (knob as NodeInput); + else if (knob is NodeOutput) + node.Outputs.Add (knob as NodeOutput); + } + } } + #endregion + + #region Working Copy + /// - /// 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 + /// 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. /// - public static void CreateWorkingCopy (ref NodeCanvas nodeCanvas, NodeEditorState[] editorStates, bool compressed) + 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 @@ -236,19 +313,16 @@ public static void CreateWorkingCopy (ref NodeCanvas nodeCanvas, NodeEditorState // Clone Node and additional scriptableObjects Node clonedNode = AddClonedSO (allSOs, clonedSOs, node); - foreach (ScriptableObject so in clonedNode.GetScriptableObjects ()) - AddClonedSO (allSOs, clonedSOs, so); - + AddClonedSOs (allSOs, clonedSOs, clonedNode.GetScriptableObjects ()); + foreach (NodeKnob knob in clonedNode.nodeKnobs) { // Clone NodeKnobs and additional scriptableObjects AddClonedSO (allSOs, clonedSOs, knob); - foreach (ScriptableObject so in knob.GetScriptableObjects ()) - AddClonedSO (allSOs, clonedSOs, so); + 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]; @@ -265,36 +339,44 @@ public static void CreateWorkingCopy (ref NodeCanvas nodeCanvas, NodeEditorState 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); - } } } - // 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) + if (editorStates) + nodeCanvas.editorStates = CreateWorkingCopy (nodeCanvas.editorStates, nodeCanvas); + + return nodeCanvas; + } + + /// + /// 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. + /// + 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++) { - for (int stateCnt = 0; stateCnt < editorStates.Length; stateCnt++) + if (editorStates[stateCnt] == null) + continue; + NodeEditorState state = editorStates[stateCnt] = Clone (editorStates[stateCnt]); + if (state == null) { - 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.connectOutput = 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; } + #region Utility + /// - /// Clones the specified SO, preserving it's name + /// Clones the specified SO, preserving its name /// private static T Clone (T SO) where T : ScriptableObject { @@ -305,7 +387,16 @@ private static T Clone (T SO) where T : ScriptableObject } /// - /// Clones SO and writes both versions into the respective list + /// Clones SO and writes both the initial and cloned versions into the respective list + /// + private static void AddClonedSOs (List scriptableObjects, List clonedScriptableObjects, ScriptableObject[] initialSOs) + { + scriptableObjects.AddRange (initialSOs); + clonedScriptableObjects.AddRange (initialSOs.Select ((ScriptableObject so) => Clone (so))); + } + + /// + /// Clones SO and writes both the initial and cloned versions into the respective list /// private static T AddClonedSO (List scriptableObjects, List clonedScriptableObjects, T initialSO) where T : ScriptableObject { @@ -318,7 +409,8 @@ private static T AddClonedSO (List scriptableObjects, List< } /// - /// First two parameters are SOs and their respective clones. Will return the saved clone of initialSO + /// First two parameters contains SOs and their respective clones. + /// Returns the clone of initialSO found in the cloned list at the respective position of initialSO in the initial list /// private static T ReplaceSO (List scriptableObjects, List clonedScriptableObjects, T initialSO) where T : ScriptableObject { @@ -331,5 +423,36 @@ private static T ReplaceSO (List scriptableObjects, List + /// 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 + /// + 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 (); + state.canvas = canvas; + canvas.editorStates = new NodeEditorState[] { state }; + #if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty (canvas); + #endif + } + state.name = stateName; + return state; + } + + #endregion } } \ No newline at end of file diff --git a/Node_Editor/Framework/NodeEditorState.cs b/Node_Editor/Framework/NodeEditorState.cs index 1d8d171d..6cb5888d 100644 --- a/Node_Editor/Framework/NodeEditorState.cs +++ b/Node_Editor/Framework/NodeEditorState.cs @@ -15,38 +15,26 @@ public class NodeEditorState : ScriptableObject // Selection State public Node selectedNode; // selected Node - [NonSerialized] - public Node focusedNode; // Node under mouse - [NonSerialized] - public NodeKnob focusedNodeKnob; // NodeKnob under mouse + [NonSerialized] public Node focusedNode; // Node under mouse + [NonSerialized] public NodeKnob focusedNodeKnob; // NodeKnob under mouse // Navigation State public Vector2 panOffset = new Vector2 (); // pan offset public float zoom = 1; // zoom; Ranges in 0.2er-steps from 0.6-2.0; applied 1/zoom; // Current Action - [NonSerialized] - public NodeOutput connectOutput; // connection this output - [NonSerialized] - public bool dragNode; // node dragging - [NonSerialized] - public bool panWindow; // window panning - [NonSerialized] - public Vector2 dragStart; // start mouse position for both node dragging and window panning - [NonSerialized] - public Vector2 dragPos; // start object position for both node dragging and window panning - [NonSerialized] - public Vector2 dragOffset; // offset for both node dragging and window panning - [NonSerialized] - public bool navigate; // navigation ('N') + [NonSerialized] public NodeOutput connectOutput; // connection this output + [NonSerialized] public bool dragNode; // node dragging + [NonSerialized] public bool panWindow; // window panning + [NonSerialized] public Vector2 dragStart; // start mouse position for both node dragging and window panning + [NonSerialized] public Vector2 dragPos; // start object position for both node dragging and window panning + [NonSerialized] public Vector2 dragOffset; // offset for both node dragging and window panning + [NonSerialized] public bool navigate; // navigation ('N') // Temporary variables - [NonSerialized] - public Rect canvasRect; // canvas Rect public Vector2 zoomPos { get { return canvasRect.size/2; } } // zoom center in canvas space - [NonSerialized] - public Vector2 zoomPanAdjust; // calculated value to offset elements with when zooming - [NonSerialized] - public List ignoreInput = new List (); // Rects inside the canvas to ignore input in (nested canvases, fE) + [NonSerialized] public Rect canvasRect; // canvas Rect + [NonSerialized] public Vector2 zoomPanAdjust; // calculated value to offset elements with when zooming + [NonSerialized] public List ignoreInput = new List (); // Rects inside the canvas to ignore input in (nested canvases, fE) } } \ No newline at end of file diff --git a/Node_Editor/RTCanvasCalculator.cs b/Node_Editor/RTCanvasCalculator.cs index d68301ec..de9c1d88 100644 --- a/Node_Editor/RTCanvasCalculator.cs +++ b/Node_Editor/RTCanvasCalculator.cs @@ -41,7 +41,7 @@ public void LoadCanvas (string path) canvasPath = path; if (!string.IsNullOrEmpty (canvasPath)) { - canvas = NodeEditorSaveManager.LoadNodeCanvas (canvasPath); + canvas = NodeEditorSaveManager.LoadNodeCanvas (canvasPath, true); CalculateCanvas (); } else diff --git a/Node_Editor/Resources/Saves/AllAround Connections Test.asset b/Node_Editor/Resources/Saves/AllAround Connections Test.asset deleted file mode 100644 index f2e381fc..00000000 Binary files a/Node_Editor/Resources/Saves/AllAround Connections Test.asset and /dev/null differ diff --git a/Node_Editor/Resources/Saves/AllAround Connections Test.asset.meta b/Node_Editor/Resources/Saves/AllAround Connections Test.asset.meta deleted file mode 100644 index ab16959e..00000000 --- a/Node_Editor/Resources/Saves/AllAround Connections Test.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b5159b85322979a4aa41c816d11380b1 -timeCreated: 1458753350 -licenseType: Free -NativeFormatImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Node_Editor/Resources/Saves/Calculation Canvas.asset b/Node_Editor/Resources/Saves/Calculation Canvas.asset index 51151e12..fa1196be 100644 Binary files a/Node_Editor/Resources/Saves/Calculation Canvas.asset and b/Node_Editor/Resources/Saves/Calculation Canvas.asset differ diff --git a/Node_Editor/Resources/Saves/Calculation Canvas.asset.meta b/Node_Editor/Resources/Saves/Calculation Canvas.asset.meta index 5e1679d7..eab0a179 100644 --- a/Node_Editor/Resources/Saves/Calculation Canvas.asset.meta +++ b/Node_Editor/Resources/Saves/Calculation Canvas.asset.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: 60480b2d075467a42b5d9e035f6d2a8f -timeCreated: 1458753477 +timeCreated: 1465329844 licenseType: Free NativeFormatImporter: userData: diff --git a/Node_Editor/RuntimeNodeEditor.cs b/Node_Editor/RuntimeNodeEditor.cs index c5ee8905..384767fb 100644 --- a/Node_Editor/RuntimeNodeEditor.cs +++ b/Node_Editor/RuntimeNodeEditor.cs @@ -1,6 +1,7 @@ using UnityEngine; using System; -using System.Collections; +using System.IO; +using System.Linq; using System.Collections.Generic; using NodeEditorFramework; @@ -19,13 +20,15 @@ public class RuntimeNodeEditor : MonoBehaviour public Rect specifiedRootRect; public Rect specifiedCanvasRect; + // GUI + private string sceneCanvasName = ""; + private Vector2 loadScenePos; + public void Start () { - if (!string.IsNullOrEmpty (canvasPath)) - LoadNodeCanvas (canvasPath); - else - NewNodeCanvas (); + NodeEditor.checkInit (false); NodeEditor.initiated = false; + LoadNodeCanvas (canvasPath); FPSCounter.Create (); } @@ -35,10 +38,14 @@ public void Update () FPSCounter.Update (); } + #region GUI + public void OnGUI () { - if (canvas != null && state != null) + if (canvas != null) { + if (state == null) + NewEditorState (); NodeEditor.checkInit (true); if (NodeEditor.InitiationError) { @@ -50,11 +57,18 @@ public void OnGUI () { if (!screenSize && specifiedRootRect.max != specifiedRootRect.min) GUI.BeginGroup (specifiedRootRect, NodeEditorGUI.nodeSkin.box); + NodeEditorGUI.StartNodeGUI (); + canvasRect = screenSize? new Rect (0, 0, Screen.width, Screen.height) : specifiedCanvasRect; canvasRect.width -= 200; state.canvasRect = canvasRect; NodeEditor.DrawCanvas (canvas, state); + + GUILayout.BeginArea (new Rect (canvasRect.x + state.canvasRect.width, state.canvasRect.y, 200, state.canvasRect.height), NodeEditorGUI.nodeSkin.box); SideGUI (); + GUILayout.EndArea (); + + NodeEditorGUI.EndNodeGUI (); if (!screenSize && specifiedRootRect.max != specifiedRootRect.min) GUI.EndGroup (); } @@ -70,19 +84,23 @@ public void OnGUI () public void SideGUI () { - GUILayout.BeginArea (new Rect (canvasRect.x + state.canvasRect.width, state.canvasRect.y, 200, state.canvasRect.height), NodeEditorGUI.nodeSkin.box); GUILayout.Label (new GUIContent ("Node Editor (" + canvas.name + ")", "The currently opened canvas in the Node Editor")); screenSize = GUILayout.Toggle (screenSize, "Adapt to Screen"); GUILayout.Label ("FPS: " + FPSCounter.currentFPS); GUILayout.Label (new GUIContent ("Node Editor (" + canvas.name + ")"), NodeEditorGUI.nodeLabelBold); - #if UNITY_EDITOR + if (GUILayout.Button (new GUIContent ("New Canvas", "Loads an empty Canvas"))) + NewNodeCanvas (); + + GUILayout.Space (6); + + #if UNITY_EDITOR if (GUILayout.Button (new GUIContent ("Save Canvas", "Saves the Canvas to a Canvas Save File in the Assets Folder"))) { string path = UnityEditor.EditorUtility.SaveFilePanelInProject ("Save Node Canvas", "Node Canvas", "asset", "", NodeEditor.editorPath + "Resources/Saves/"); if (!string.IsNullOrEmpty (path)) - NodeEditorSaveManager.SaveNodeCanvas (path, true, canvas, state); + NodeEditorSaveManager.SaveNodeCanvas (path, canvas, true); } if (GUILayout.Button (new GUIContent ("Load Canvas", "Loads the Canvas from a Canvas Save File in the Assets Folder"))) @@ -99,11 +117,31 @@ public void SideGUI () LoadNodeCanvas (path); } } + GUILayout.Space (6); + #endif - #endif + GUILayout.BeginHorizontal (); + sceneCanvasName = GUILayout.TextField (sceneCanvasName, GUILayout.ExpandWidth (true)); + if (GUILayout.Button (new GUIContent ("Save to Scene", "Saves the Canvas to the Scene"), GUILayout.ExpandWidth (false))) + { + SaveSceneNodeCanvas (sceneCanvasName); + } + GUILayout.EndHorizontal (); - if (GUILayout.Button (new GUIContent ("New Canvas", "Loads an empty Canvas"))) - NewNodeCanvas (); + if (GUILayout.Button (new GUIContent ("Load from Scene", "Loads the Canvas from the Scene"))) + { + NodeEditorFramework.Utilities.GenericMenu menu = new NodeEditorFramework.Utilities.GenericMenu (); + foreach (string sceneSave in NodeEditorSaveManager.GetSceneSaves ()) + menu.AddItem (new GUIContent (sceneSave), false, LoadSceneCanvasCallback, (object)sceneSave); + menu.Show (loadScenePos); + } + if (Event.current.type == EventType.Repaint) + { + Rect popupPos = GUILayoutUtility.GetLastRect (); + loadScenePos = new Vector2 (popupPos.x+2, popupPos.yMax+2); + } + + GUILayout.Space (6); if (GUILayout.Button (new GUIContent ("Recalculate All", "Initiates complete recalculate. Usually does not need to be triggered manually."))) NodeEditor.RecalculateAll (canvas); @@ -113,41 +151,63 @@ public void SideGUI () NodeEditorGUI.knobSize = RTEditorGUI.IntSlider (new GUIContent ("Handle Size", "The size of the Node Input/Output handles"), NodeEditorGUI.knobSize, 12, 20); state.zoom = RTEditorGUI.Slider (new GUIContent ("Zoom", "Use the Mousewheel. Seriously."), state.zoom, 0.6f, 2); + } - GUILayout.EndArea (); + #endregion + + #region Canvas management + + private void LoadSceneCanvasCallback (object save) + { + LoadSceneNodeCanvas ((string)save); } - public void LoadNodeCanvas (string path) + public void SaveSceneNodeCanvas (string path) + { + canvas.editorStates = new NodeEditorState[] { state }; + NodeEditorSaveManager.SaveSceneNodeCanvas (path, ref canvas, true); + } + + public void LoadSceneNodeCanvas (string path) { - // Load the NodeCanvas - canvas = NodeEditorSaveManager.LoadNodeCanvas (path); - if (canvas == null) + // Try to load the NodeCanvas + if ((canvas = NodeEditorSaveManager.LoadSceneNodeCanvas (path, true)) == null) { NewNodeCanvas (); return; } + state = NodeEditorSaveManager.ExtractEditorState (canvas, "MainEditorState"); + + NodeEditor.RecalculateAll (canvas); + } - // Load the associated MainEditorState - List editorStates = NodeEditorSaveManager.LoadEditorStates (path); - if (editorStates.Count == 0) - state = ScriptableObject.CreateInstance (); - else + public void LoadNodeCanvas (string path) + { + if (!File.Exists (path) || (canvas = NodeEditorSaveManager.LoadNodeCanvas (path, true)) == null) { - state = editorStates.Find (x => x.name == "MainEditorState"); - if (state == null) state = editorStates[0]; + NewNodeCanvas (); + return; } - state.canvas = canvas; - + state = NodeEditorSaveManager.ExtractEditorState (canvas, "MainEditorState"); NodeEditor.RecalculateAll (canvas); } public void NewNodeCanvas () { canvas = ScriptableObject.CreateInstance (); + canvas.name = "New Canvas"; + NewEditorState (); + } + + private void NewEditorState () + { state = ScriptableObject.CreateInstance (); state.canvas = canvas; state.name = "MainEditorState"; + canvas.editorStates = new NodeEditorState[] { state }; } + + #endregion } } diff --git a/Node_Editor/Utilities/GUI/GUIScaleUtility.cs b/Node_Editor/Utilities/GUI/GUIScaleUtility.cs index 2c1e841d..31c0a3cb 100644 --- a/Node_Editor/Utilities/GUI/GUIScaleUtility.cs +++ b/Node_Editor/Utilities/GUI/GUIScaleUtility.cs @@ -47,24 +47,6 @@ public static void Init () Assembly UnityEngine = Assembly.GetAssembly (typeof (UnityEngine.GUI)); Type GUIClipType = UnityEngine.GetType ("UnityEngine.GUIClip", true); -// string log = "Members without Bindflags: "; -// foreach (MemberInfo member in GUIClipType.GetMembers ()) -// log += member.MemberType + "-" + member.Name + " |-| "; -// -// log += Environment.NewLine + "Both NonPublic and Public Instance Members: "; -// foreach (MemberInfo member in GUIClipType.GetMembers (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) -// log += member.MemberType + "-" + member.Name + " |-| "; -// -// log += Environment.NewLine + "Nonpublic Static Members: "; -// foreach (MemberInfo member in GUIClipType.GetMembers (BindingFlags.Static | BindingFlags.NonPublic)) -// log += member.MemberType + "-" + member.Name + " |-| "; -// -// log += Environment.NewLine + "Public Static Members: "; -// foreach (MemberInfo member in GUIClipType.GetMembers (BindingFlags.Static | BindingFlags.Public)) -// log += member.MemberType + "-" + member.Name + " |-| "; -// -// Debug.Log (log); - PropertyInfo topmostRect = GUIClipType.GetProperty ("topmostRect", BindingFlags.Static | BindingFlags.Public); MethodInfo GetTopRect = GUIClipType.GetMethod ("GetTopRect", BindingFlags.Static | BindingFlags.NonPublic); MethodInfo ClipRect = GUIClipType.GetMethod ("Clip", BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, new Type[] { typeof(Rect) }, new ParameterModifier[] {}); @@ -82,6 +64,15 @@ public static void Init () GetTopRectDelegate = (Func)Delegate.CreateDelegate (typeof(Func), GetTopRect); topmostRectDelegate = (Func)Delegate.CreateDelegate (typeof(Func), topmostRect.GetGetMethod ()); + if (GetTopRectDelegate == null || topmostRectDelegate == null) + { + Debug.LogWarning ("GUIScaleUtility cannot run on this system! Compability mode enabled. For you that means you're not able to use the Node Editor inside more than one group:( Please PM me (Seneral @UnityForums) so I can figure out what causes this! Thanks!"); + Debug.LogWarning ((GUIClipType == null? "GUIClipType is Null, " : "") + (topmostRect == null? "topmostRect is Null, " : "") + (GetTopRect == null? "GetTopRect is Null, " : "") + (ClipRect == null? "ClipRect is Null, " : "")); + compabilityMode = true; + initiated = true; + return; + } + // As we can call Begin/Ends inside another, we need to save their states hierarchial in Lists (not Stack, as we need to iterate over them!): currentRectStack = new List (); rectStackGroups = new List> (); @@ -89,7 +80,7 @@ public static void Init () adjustedGUILayout = new List (); // Sometimes, strange errors pop up (related to Mac?), which we try to catch and enable a compability Mode no supporting zooming in groups - try + /*try { topmostRectDelegate.Invoke (); } @@ -98,8 +89,8 @@ public static void Init () Debug.LogWarning ("GUIScaleUtility cannot run on this system! Compability mode enabled. For you that means you're not able to use the Node Editor inside more than one group:( Please PM me (Seneral @UnityForums) so I can figure out what causes this! Thanks!"); Debug.Log (e.Message); compabilityMode = true; - } - + }*/ + initiated = true; } diff --git a/Node_Editor/Utilities/GUI/OverlayGUI.cs b/Node_Editor/Utilities/GUI/OverlayGUI.cs index 01fff891..37f73538 100644 --- a/Node_Editor/Utilities/GUI/OverlayGUI.cs +++ b/Node_Editor/Utilities/GUI/OverlayGUI.cs @@ -79,7 +79,9 @@ public void Show (Vector2 pos) selectedPath = ""; OverlayGUI.currentPopup = this; } - + + public Vector2 Position { get { return position.position; } } + #region Creation public void AddItem (GUIContent content, bool on, MenuFunctionData func, object userData) @@ -334,6 +336,8 @@ public void Execute () public class GenericMenu { private static PopupMenu popup; + + public Vector2 Position { get { return popup.Position; } } public GenericMenu () { @@ -342,14 +346,12 @@ public GenericMenu () public void ShowAsContext () { - Vector2 mousePos = Event.current.mousePosition; - //Vector2 screenMousePos = GUIScaleUtility.GUIToScreenSpace (mousePos); - popup.Show (mousePos); + popup.Show (GUIScaleUtility.GUIToScreenSpace (Event.current.mousePosition)); } public void Show (Vector2 pos) { - popup.Show (pos); + popup.Show (GUIScaleUtility.GUIToScreenSpace (pos)); } public void AddItem (GUIContent content, bool on, PopupMenu.MenuFunctionData func, object userData) diff --git a/Node_Editor/Utilities/ResourceManager.cs b/Node_Editor/Utilities/ResourceManager.cs index 0e943094..b83a2959 100644 --- a/Node_Editor/Utilities/ResourceManager.cs +++ b/Node_Editor/Utilities/ResourceManager.cs @@ -26,25 +26,17 @@ public static void SetDefaultResourcePath (string defaultResourcePath) public static string PreparePath (string path) { path = path.Replace (Application.dataPath, "Assets"); - #if UNITY_EDITOR - if (!path.StartsWith ("Assets/")) - path = _ResourcePath + path; - #else + #if UNITY_EDITOR + if (!Application.isPlaying) + { + if (!path.StartsWith ("Assets/")) + path = _ResourcePath + path; + return path; + } + #endif if (path.Contains ("Resources")) path = path.Substring (path.LastIndexOf ("Resources") + 10); - path = path.Substring (0, path.LastIndexOf ('.')); - #endif -// if (Application.isPlaying) -// { // At runtime -// if (path.Contains ("Resources")) -// path = path.Substring (path.LastIndexOf ("Resources") + 10); -// path = path.Substring (0, path.LastIndexOf ('.')); -// return path; -// } -// // In the editor -// if (!path.StartsWith ("Assets/")) -// path = _ResourcePath + path; - return path; + return path.Substring (0, path.LastIndexOf ('.')); } /// @@ -54,15 +46,12 @@ public static string PreparePath (string path) public static T[] LoadResources (string path) where T : UnityEngine.Object { path = PreparePath (path); - //if (Application.isPlaying) // At runtime - //return UnityEngine.Resources.LoadAll (path); - #if UNITY_EDITOR - // In the editor - return UnityEditor.AssetDatabase.LoadAllAssetsAtPath (path).OfType ().ToArray (); - #else - throw new NotImplementedException ("Currently it is not possible to load subAssets at runtime!"); - return UnityEngine.Resources.LoadAll (path); + #if UNITY_EDITOR // In the editor + if (!Application.isPlaying) + return UnityEditor.AssetDatabase.LoadAllAssetsAtPath (path).OfType ().ToArray (); #endif + throw new NotImplementedException ("Currently it is not possible to load subAssets at runtime!"); + // return UnityEngine.Resources.LoadAll (path); } /// @@ -72,18 +61,15 @@ public static T[] LoadResources (string path) where T : UnityEngine.Object public static T LoadResource (string path) where T : UnityEngine.Object { path = PreparePath (path); - if (Application.isPlaying) // At runtime - return UnityEngine.Resources.Load (path); - #if UNITY_EDITOR - // In the editor - return UnityEditor.AssetDatabase.LoadAssetAtPath (path); - #else - return null; + #if UNITY_EDITOR // In the editor + if (!Application.isPlaying) + return UnityEditor.AssetDatabase.LoadAssetAtPath (path); #endif + return UnityEngine.Resources.Load (path); } #endregion - + #region Texture Management private static List loadedTextures = new List ();