diff --git a/Editor/Node_Editor/NodeEditorWindow.cs b/Editor/Node_Editor/NodeEditorWindow.cs
index c45a8ad1..e02cb31f 100644
--- a/Editor/Node_Editor/NodeEditorWindow.cs
+++ b/Editor/Node_Editor/NodeEditorWindow.cs
@@ -5,8 +5,8 @@
using UnityEngine;
using UnityEditor;
-using NodeEditorFramework;
using NodeEditorFramework.Utilities;
+using NodeEditorFramework.IO;
using GenericMenu = UnityEditor.GenericMenu;
@@ -30,14 +30,23 @@ public class NodeEditorWindow : EditorWindow
private int sideWindowWidth = 400;
private int toolbarHeight = 17;
- private Rect modalWindowRect = new Rect(20, 50, 250, 100);
-
+ // Modal Panel
private bool showSideWindow;
private bool showModalPanel;
+ private Rect modalPanelRect = new Rect(20, 50, 250, 70);
+ private Action modalPanelContent;
public Rect sideWindowRect { get { return new Rect (position.width - sideWindowWidth, toolbarHeight, sideWindowWidth, position.height); } }
public Rect canvasWindowRect { get { return new Rect(0, toolbarHeight, position.width - (showSideWindow? sideWindowWidth : 0), position.height - toolbarHeight); } }
+ // IO Format modal panel
+ private ImportExportFormat IOFormat;
+ private object[] IOLocationArgs;
+ private delegate bool? DefExportLocationGUI(string canvasName, ref object[] locationArgs);
+ private delegate bool? DefImportLocationGUI(ref object[] locationArgs);
+ private DefImportLocationGUI ImportLocationGUI;
+ private DefExportLocationGUI ExportLocationGUI;
+
#region General
///
@@ -135,26 +144,6 @@ private void DrawSceneGUI()
SceneView.lastActiveSceneView.Repaint();
}
- private void DoModalWindow(int unusedWindowID)
- {
- GUILayout.BeginHorizontal ();
- sceneCanvasName = GUILayout.TextField (sceneCanvasName, GUILayout.ExpandWidth (true));
- bool overwrite = NodeEditorSaveManager.HasSceneSave (sceneCanvasName);
- if (overwrite)
- GUILayout.Label (new GUIContent ("!!!", "A canvas with the specified name already exists. It will be overwritten!"), GUILayout.ExpandWidth (false));
- GUILayout.EndHorizontal ();
-
- GUILayout.BeginHorizontal ();
- if (GUILayout.Button("Cancel"))
- showModalPanel = false;
- if (GUILayout.Button (new GUIContent (overwrite? "Overwrite" : "Save", "Save the canvas to the Scene")))
- {
- showModalPanel = false;
- canvasCache.SaveSceneNodeCanvas (sceneCanvasName);
- }
- GUILayout.EndHorizontal ();
- }
-
private void OnGUI()
{
// Initiation
@@ -193,6 +182,9 @@ private void OnGUI()
// Draw Toolbar
DrawToolbarGUI();
+ // Draw Modal Panel
+ DrawModalPanel ();
+
if (showSideWindow)
{ // Draw Side Window
sideWindowWidth = Math.Min(600, Math.Max(200, (int)(position.width / 5)));
@@ -201,13 +193,6 @@ private void OnGUI()
GUILayout.EndArea();
}
- if (showModalPanel)
- {
- BeginWindows();
- modalWindowRect = GUILayout.Window(0, modalWindowRect, DoModalWindow, "Save to Scene");
- EndWindows();
- }
-
NodeEditorGUI.EndNodeGUI();
}
@@ -222,7 +207,7 @@ private void DrawToolbarGUI()
// Canvas creation
NodeCanvasManager.FillCanvasTypeMenu(ref menu, CreateCanvas, "New Canvas/");
- menu.AddSeparator("");
+ menu.AddSeparator("");
// Scene Saving
menu.AddItem(new GUIContent("Load Canvas", "Loads an asset canvas"), false, LoadCanvas);
@@ -232,13 +217,18 @@ private void DrawToolbarGUI()
menu.AddItem(new GUIContent("Save Canvas As"), false, SaveCanvasAs);
menu.AddSeparator("");
+ // Import / Export filled with import/export types
+ ImportExportManager.FillImportFormatMenu(ref menu, ImportCanvasCallback, "Import/");
+ ImportExportManager.FillExportFormatMenu(ref menu, ExportCanvasCallback, "Export/");
+ menu.AddSeparator("");
+
// Scene Saving
foreach (string sceneSave in NodeEditorSaveManager.GetSceneSaves())
{
if (sceneSave.ToLower () != "lastsession")
- menu.AddItem(new GUIContent("Load Canvas from Scene/" + sceneSave), false, LoadSceneCanvasCallback, sceneSave);
+ menu.AddItem(new GUIContent("Load Canvas from Scene/" + sceneSave), false, LoadSceneCanvasCallback, sceneSave);
}
- menu.AddItem( new GUIContent("Save Canvas to Scene"), false, () => showModalPanel = true);
+ menu.AddItem( new GUIContent("Save Canvas to Scene"), false, SaveSceneCanvasCallback);
menu.DropDown (new Rect (5, toolbarHeight, 0, 0));
}
@@ -261,12 +251,47 @@ private void DrawToolbarGUI()
GUI.backgroundColor = new Color(1, 0.3f, 0.3f, 1);
if (GUILayout.Button("Force Re-init", EditorStyles.toolbarButton, GUILayout.Width(80)))
- NodeEditor.ReInit (true);
+ NodeEditor.ReInit(true);
GUI.backgroundColor = Color.white;
EditorGUILayout.EndHorizontal();
}
+ private void SaveSceneCanvasPanel()
+ {
+ GUILayout.Label("Save Canvas To Scene");
+
+ GUILayout.BeginHorizontal();
+ sceneCanvasName = GUILayout.TextField(sceneCanvasName, GUILayout.ExpandWidth(true));
+ bool overwrite = NodeEditorSaveManager.HasSceneSave(sceneCanvasName);
+ if (overwrite)
+ GUILayout.Label(new GUIContent("!!!", "A canvas with the specified name already exists. It will be overwritten!"), GUILayout.ExpandWidth(false));
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Cancel"))
+ showModalPanel = false;
+ if (GUILayout.Button(new GUIContent(overwrite ? "Overwrite" : "Save", "Save the canvas to the Scene")))
+ {
+ showModalPanel = false;
+ if (!string.IsNullOrEmpty (sceneCanvasName))
+ canvasCache.SaveSceneNodeCanvas(sceneCanvasName);
+ }
+ GUILayout.EndHorizontal();
+ }
+
+ public void DrawModalPanel()
+ {
+ if (showModalPanel)
+ {
+ if (modalPanelContent == null)
+ return;
+ GUILayout.BeginArea(modalPanelRect, NodeEditorGUI.nodeBox);
+ modalPanelContent.Invoke();
+ GUILayout.EndArea();
+ }
+ }
+
private void DrawSideWindow()
{
GUILayout.Label (new GUIContent ("" + canvasCache.nodeCanvas.saveName + " (" + (canvasCache.nodeCanvas.livesInScene? "Scene Save" : "Asset Save") + ")", "Opened Canvas path: " + canvasCache.nodeCanvas.savePath), NodeEditorGUI.nodeLabelBold);
@@ -374,12 +399,12 @@ private void DrawSideWindow()
#endregion
- #region Menu Callbacks
+#region Menu Callbacks
private void CreateCanvas(Type type)
{
editor.canvasCache.NewNodeCanvas(type);
- }
+ }
private void LoadCanvas()
{
@@ -390,37 +415,37 @@ private void LoadCanvas()
ShowNotification(new GUIContent("You should select an asset inside your project folder!"));
}
else
- canvasCache.LoadNodeCanvas (path);
+ canvasCache.LoadNodeCanvas(path);
}
private void ReloadCanvas()
{
string path = canvasCache.nodeCanvas.savePath;
- if (!string.IsNullOrEmpty (path))
+ if (!string.IsNullOrEmpty(path))
{
- if (path.StartsWith ("SCENE/"))
- canvasCache.LoadSceneNodeCanvas (path.Substring (6));
+ if (path.StartsWith("SCENE/"))
+ canvasCache.LoadSceneNodeCanvas(path.Substring(6));
else
- canvasCache.LoadNodeCanvas (path);
- ShowNotification (new GUIContent ("Canvas Reloaded!"));
+ canvasCache.LoadNodeCanvas(path);
+ ShowNotification(new GUIContent("Canvas Reloaded!"));
}
else
- ShowNotification (new GUIContent ("Cannot reload canvas as it has not been saved yet!"));
+ ShowNotification(new GUIContent("Cannot reload canvas as it has not been saved yet!"));
}
private void SaveCanvas()
{
string path = canvasCache.nodeCanvas.savePath;
- if (!string.IsNullOrEmpty (path))
+ if (!string.IsNullOrEmpty(path))
{
- if (path.StartsWith ("SCENE/"))
- canvasCache.SaveSceneNodeCanvas (path.Substring (6));
+ if (path.StartsWith("SCENE/"))
+ canvasCache.SaveSceneNodeCanvas(path.Substring(6));
else
- canvasCache.SaveNodeCanvas (path);
- ShowNotification (new GUIContent ("Canvas Saved!"));
+ canvasCache.SaveNodeCanvas(path);
+ ShowNotification(new GUIContent("Canvas Saved!"));
}
else
- ShowNotification (new GUIContent ("No save location found. Use 'Save As'!"));
+ ShowNotification(new GUIContent("No save location found. Use 'Save As'!"));
}
private void SaveCanvasAs()
@@ -429,17 +454,88 @@ private void SaveCanvasAs()
if (canvasCache.nodeCanvas != null && !string.IsNullOrEmpty(canvasCache.nodeCanvas.savePath))
panelPath = canvasCache.nodeCanvas.savePath;
string path = EditorUtility.SaveFilePanelInProject ("Save Node Canvas", "Node Canvas", "asset", "", panelPath);
- if (!string.IsNullOrEmpty (path))
- canvasCache.SaveNodeCanvas (path);
+ if (!string.IsNullOrEmpty(path))
+ canvasCache.SaveNodeCanvas(path);
}
- public void LoadSceneCanvasCallback (object canvas)
+ private void LoadSceneCanvasCallback (object canvas)
{
- canvasCache.LoadSceneNodeCanvas ((string)canvas);
+ canvasCache.LoadSceneNodeCanvas((string)canvas);
//Atheos auto save the sceneCanvasName to (sava to scence) after (Load from scene)
- sceneCanvasName = canvasCache.nodeCanvas.name;
+ sceneCanvasName = canvasCache.nodeCanvas.name;
}
- #endregion
+ private void SaveSceneCanvasCallback()
+ {
+ modalPanelContent = SaveSceneCanvasPanel;
+ showModalPanel = true;
+ }
+
+
+ private void ImportCanvasCallback(string formatID)
+ {
+ IOFormat = ImportExportManager.ParseFormat(formatID);
+ if (IOFormat.RequiresLocationGUI)
+ {
+ ImportLocationGUI = IOFormat.ImportLocationArgsGUI;
+ modalPanelContent = ImportCanvasGUI;
+ showModalPanel = true;
+ }
+ else if (IOFormat.ImportLocationArgsSelection(out IOLocationArgs))
+ canvasCache.SetCanvas(ImportExportManager.ImportCanvas(IOFormat, IOLocationArgs));
+ }
+
+ private void ImportCanvasGUI()
+ {
+ if (ImportLocationGUI != null)
+ {
+ bool? state = ImportLocationGUI(ref IOLocationArgs);
+ if (state == null)
+ return;
+
+ if (state == true)
+ canvasCache.SetCanvas(ImportExportManager.ImportCanvas(IOFormat, IOLocationArgs));
+
+ ImportLocationGUI = null;
+ modalPanelContent = null;
+ showModalPanel = false;
+ }
+ else
+ showModalPanel = false;
+ }
+
+ private void ExportCanvasCallback(string formatID)
+ {
+ IOFormat = ImportExportManager.ParseFormat(formatID);
+ if (IOFormat.RequiresLocationGUI)
+ {
+ ExportLocationGUI = IOFormat.ExportLocationArgsGUI;
+ modalPanelContent = ExportCanvasGUI;
+ showModalPanel = true;
+ }
+ else if (IOFormat.ExportLocationArgsSelection(canvasCache.nodeCanvas.saveName, out IOLocationArgs))
+ ImportExportManager.ExportCanvas(canvasCache.nodeCanvas, IOFormat, IOLocationArgs);
+ }
+
+ private void ExportCanvasGUI()
+ {
+ if (ExportLocationGUI != null)
+ {
+ bool? state = ExportLocationGUI(canvasCache.nodeCanvas.saveName, ref IOLocationArgs);
+ if (state == null)
+ return;
+
+ if (state == true)
+ ImportExportManager.ExportCanvas(canvasCache.nodeCanvas, IOFormat, IOLocationArgs);
+
+ ImportLocationGUI = null;
+ modalPanelContent = null;
+ showModalPanel = false;
+ }
+ else
+ showModalPanel = false;
+ }
+
+#endregion
}
}
diff --git a/Node_Editor/Default/XMLImportExport.cs b/Node_Editor/Default/XMLImportExport.cs
new file mode 100644
index 00000000..ffc243ae
--- /dev/null
+++ b/Node_Editor/Default/XMLImportExport.cs
@@ -0,0 +1,290 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Xml.XPath;
+using UnityEngine;
+
+namespace NodeEditorFramework.IO
+{
+ public class XMLImportExport : StructuredImportExportFormat
+ {
+ public override string FormatIdentifier { get { return "XML"; } }
+ public override string FormatExtension { get { return "xml"; } }
+
+ public override void ExportData (CanvasData data, params object[] args)
+ {
+ if (args == null || args.Length != 1 || args[0].GetType () != typeof(string))
+ throw new ArgumentException ("Location Arguments");
+ string path = (string)args[0];
+
+ XmlDocument saveDoc = new XmlDocument();
+ XmlDeclaration decl = saveDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
+ saveDoc.InsertBefore(decl, saveDoc.DocumentElement);
+
+ // CANVAS
+
+ XmlElement canvas = saveDoc.CreateElement("NodeCanvas");
+ canvas.SetAttribute("type", data.type.FullName);
+ saveDoc.AppendChild(canvas);
+
+ // EDITOR STATES
+
+ XmlElement editorStates = saveDoc.CreateElement("EditorStates");
+ canvas.AppendChild(editorStates);
+ foreach (EditorStateData stateData in data.editorStates)
+ {
+ XmlElement editorState = saveDoc.CreateElement("EditorState");
+ editorState.SetAttribute("selected", stateData.selectedNode != null ? stateData.selectedNode.nodeID.ToString() : "");
+ editorState.SetAttribute("pan", stateData.panOffset.x + "," + stateData.panOffset.y);
+ editorState.SetAttribute("zoom", stateData.zoom.ToString());
+ editorStates.AppendChild(editorState);
+ }
+
+ // NODES
+
+ XmlElement nodes = saveDoc.CreateElement("Nodes");
+ canvas.AppendChild(nodes);
+ foreach (NodeData nodeData in data.nodes.Values)
+ {
+ XmlElement node = saveDoc.CreateElement("Node");
+ node.SetAttribute("ID", nodeData.nodeID.ToString());
+ node.SetAttribute("type", nodeData.typeID);
+ node.SetAttribute("pos", nodeData.nodePos.x + "," + nodeData.nodePos.y);
+ nodes.AppendChild(node);
+ // Write port records
+ //XmlElement connectionPorts = saveDoc.CreateElement("ConnectionPorts");
+ //node.AppendChild(connectionPorts);
+ foreach (PortData portData in nodeData.connectionPorts)
+ {
+ XmlElement port = saveDoc.CreateElement("Port");
+ port.SetAttribute("ID", portData.portID.ToString ());
+ port.SetAttribute("varName", portData.varName);
+ node.AppendChild(port);
+ // Connections
+ /*foreach (PortData conData in portData.connections)
+ { // TODO: Write immediate connections. Not needed, only for readability.
+ XmlElement connection = saveDoc.CreateElement("Connection");
+ connection.SetAttribute("ID", conData.portID.ToString());
+ port.AppendChild(connection);
+ }*/
+ }
+ // Write variable data
+ //XmlElement variables = saveDoc.CreateElement("Variables");
+ //node.AppendChild(variables);
+ foreach (VariableData varData in nodeData.variables)
+ {
+ XmlElement variable = saveDoc.CreateElement("Variable");
+ variable.SetAttribute("name", varData.name);
+ node.AppendChild(variable);
+ if (varData.refObject != null)
+ variable.SetAttribute("refID", varData.refObject.refID.ToString());
+ else
+ { // Serialize value and append
+ variable.SetAttribute("type", varData.value.GetType ().FullName);
+ SerializeObjectToXML(variable, varData.value);
+ }
+ }
+ }
+
+ // CONNECTIONS
+
+ XmlElement connections = saveDoc.CreateElement("Connections");
+ canvas.AppendChild(connections);
+ foreach (ConnectionData connectionData in data.connections)
+ {
+ XmlElement connection = saveDoc.CreateElement("Connection");
+ connection.SetAttribute("port1ID", connectionData.port1.portID.ToString ());
+ connection.SetAttribute("port2ID", connectionData.port2.portID.ToString ());
+ connections.AppendChild(connection);
+ }
+
+ // OBJECTS
+
+ XmlElement objects = saveDoc.CreateElement("Objects");
+ canvas.AppendChild(objects);
+ foreach (ObjectData objectData in data.objects.Values)
+ {
+ XmlElement obj = saveDoc.CreateElement("Object");
+ obj.SetAttribute("refID", objectData.refID.ToString());
+ obj.SetAttribute("type", objectData.data.GetType().FullName);
+ objects.AppendChild(obj);
+ SerializeObjectToXML(obj, objectData.data);
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ using (XmlTextWriter writer = new XmlTextWriter (path, Encoding.UTF8))
+ {
+ writer.Formatting = Formatting.Indented;
+ writer.Indentation = 1;
+ writer.IndentChar = '\t';
+ saveDoc.Save (writer);
+ }
+ }
+
+ public override CanvasData ImportData (params object[] args)
+ {
+ if (args == null || args.Length != 1 || args[0].GetType () != typeof(string))
+ throw new ArgumentException ("Location Arguments");
+ string path = (string)args[0];
+
+ using (FileStream fs = new FileStream (path, FileMode.Open))
+ {
+ XmlDocument data = new XmlDocument ();
+ data.Load (fs);
+
+ // CANVAS
+
+ string canvasName = Path.GetFileNameWithoutExtension(path);
+ XmlElement xmlCanvas = (XmlElement)data.SelectSingleNode ("//NodeCanvas");
+ Type canvasType = NodeCanvasManager.GetCanvasTypeData (xmlCanvas.GetAttribute ("type")).CanvasType;
+ if (canvasType == null)
+ throw new XmlException ("Could not find NodeCanvas of type '" + xmlCanvas.GetAttribute ("type") + "'!");
+ CanvasData canvasData = new CanvasData (canvasType, canvasName);
+ Dictionary ports = new Dictionary();
+
+ // OBJECTS
+
+ IEnumerable xmlObjects = xmlCanvas.SelectNodes("Objects/Object").OfType();
+ foreach (XmlElement xmlObject in xmlObjects)
+ {
+ int refID = GetIntegerAttribute(xmlObject, "refID");
+ string typeName = xmlObject.GetAttribute("type");
+ Type type = Type.GetType(typeName, true);
+ object obj = DeserializeObjectFromXML(xmlObject, type);
+ ObjectData objData = new ObjectData(refID, obj);
+ canvasData.objects.Add(refID, objData);
+ }
+
+ // NODES
+
+ IEnumerable xmlNodes = xmlCanvas.SelectNodes("Nodes/Node").OfType();
+ foreach (XmlElement xmlNode in xmlNodes)
+ {
+ int nodeID = GetIntegerAttribute(xmlNode, "ID");
+ string typeID = xmlNode.GetAttribute("type");
+ Vector2 nodePos = GetVectorAttribute(xmlNode, "pos");
+ // Record
+ NodeData node = new NodeData(typeID, nodeID, nodePos);
+ canvasData.nodes.Add(nodeID, node);
+ // Validate and record ports
+ IEnumerable xmlConnectionPorts = xmlNode.SelectNodes("Port").OfType();
+ foreach (XmlElement xmlPort in xmlConnectionPorts)
+ {
+ int portID = GetIntegerAttribute(xmlPort, "ID");
+ string varName = xmlPort.GetAttribute("varName");
+ PortData port = new PortData(node, varName, portID);
+ node.connectionPorts.Add(port);
+ ports.Add(portID, port);
+ }
+ // Read in variable data
+ IEnumerable xmlVariables = xmlNode.SelectNodes("Variable").OfType();
+ foreach (XmlElement xmlVariable in xmlVariables)
+ {
+ string varName = xmlVariable.GetAttribute("name");
+ VariableData varData = new VariableData(varName);
+ if (xmlVariable.HasAttribute("refID"))
+ { // Read from objects
+ int refID = GetIntegerAttribute(xmlVariable, "refID");
+ ObjectData objData;
+ if (canvasData.objects.TryGetValue(refID, out objData))
+ varData.refObject = objData;
+ }
+ else
+ { // Read directly
+ string typeName = xmlVariable.GetAttribute("type");
+ Type type = Type.GetType(typeName, true);
+ varData.value = DeserializeObjectFromXML(xmlVariable, type);
+ }
+ node.variables.Add(varData);
+ }
+ }
+
+ // CONNECTIONS
+
+ IEnumerable xmlConnections = xmlCanvas.SelectNodes("Connections/Connection").OfType();
+ foreach (XmlElement xmlConnection in xmlConnections)
+ {
+ int port1ID = GetIntegerAttribute(xmlConnection, "port1ID");
+ int port2ID = GetIntegerAttribute(xmlConnection, "port2ID");
+ PortData port1, port2;
+ if (ports.TryGetValue(port1ID, out port1) && ports.TryGetValue(port2ID, out port2))
+ canvasData.RecordConnection(port1, port2);
+ }
+
+ // EDITOR STATES
+
+ IEnumerable xmlEditorStates = xmlCanvas.SelectNodes("EditorStates/EditorState").OfType();
+ List editorStates = new List();
+ foreach (XmlElement xmlEditorState in xmlEditorStates)
+ {
+ Vector2 pan = GetVectorAttribute(xmlEditorState, "pan");
+ float zoom;
+ if (!float.TryParse(xmlEditorState.GetAttribute("zoom"), out zoom))
+ zoom = 1;
+ // Selected Node
+ NodeData selectedNode = null;
+ int selectedNodeID;
+ if (int.TryParse(xmlEditorState.GetAttribute("selected"), out selectedNodeID))
+ selectedNode = canvasData.FindNode(selectedNodeID);
+ // Create state
+ editorStates.Add(new EditorStateData(selectedNode, pan, zoom));
+ }
+ canvasData.editorStates = editorStates.ToArray();
+
+ return canvasData;
+ }
+ }
+
+ #region Utility
+
+ private void SerializeObjectToXML(XmlElement parent, object obj)
+ {
+ XmlSerializer serializer = new XmlSerializer(obj.GetType());
+ XPathNavigator navigator = parent.CreateNavigator();
+ using (XmlWriter writer = navigator.AppendChild())
+ serializer.Serialize(writer, obj);
+ }
+
+ private object DeserializeObjectFromXML(XmlElement parent, Type type)
+ {
+ if (!parent.HasChildNodes)
+ return null;
+ XmlSerializer serializer = new XmlSerializer(type);
+ XPathNavigator navigator = parent.FirstChild.CreateNavigator();
+ using (XmlReader reader = navigator.ReadSubtree())
+ return serializer.Deserialize(reader);
+ }
+
+ private int GetIntegerAttribute(XmlElement element, string attribute, bool throwIfInvalid = true)
+ {
+ int result = 0;
+ if (!int.TryParse(element.GetAttribute(attribute), out result) && throwIfInvalid)
+ throw new XmlException("Invalid " + attribute + " for element " + element.Name + "!");
+ return result;
+ }
+
+ private float GetFloatAttribute(XmlElement element, string attribute, bool throwIfInvalid = true)
+ {
+ float result = 0;
+ if (!float.TryParse(element.GetAttribute(attribute), out result) && throwIfInvalid)
+ throw new XmlException("Invalid " + attribute + " for element " + element.Name + "!");
+ return result;
+ }
+
+ private Vector2 GetVectorAttribute(XmlElement element, string attribute, bool throwIfInvalid = true)
+ {
+ string[] vecString = element.GetAttribute(attribute).Split(',');
+ Vector2 vector = new Vector2(0, 0);
+ float vecX, vecY;
+ if (vecString.Length == 2 && float.TryParse(vecString[0], out vecX) && float.TryParse(vecString[1], out vecY))
+ vector = new Vector2(vecX, vecY);
+ return vector;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Node_Editor/Default/XMLImportExport.cs.meta b/Node_Editor/Default/XMLImportExport.cs.meta
new file mode 100644
index 00000000..e584efe0
--- /dev/null
+++ b/Node_Editor/Default/XMLImportExport.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 4b4fed10ebd11f149aca4caaba5a4c9b
+timeCreated: 1497983217
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Node_Editor/Framework/Core/NodeEditorState.cs b/Node_Editor/Framework/Core/NodeEditorState.cs
index 041fdd06..0182cfa5 100644
--- a/Node_Editor/Framework/Core/NodeEditorState.cs
+++ b/Node_Editor/Framework/Core/NodeEditorState.cs
@@ -11,7 +11,7 @@ public partial class NodeEditorState : ScriptableObject
public NodeEditorState parentEditor;
// Canvas options
- public bool drawing = true; // whether to draw the canvas
+ [NonSerialized] public bool drawing = true; // whether to draw the canvas
// Selection State
public Node selectedNode; // selected Node
diff --git a/Node_Editor/Framework/NodeEditor.cs b/Node_Editor/Framework/NodeEditor.cs
index 8411db40..1ffd21d9 100644
--- a/Node_Editor/Framework/NodeEditor.cs
+++ b/Node_Editor/Framework/NodeEditor.cs
@@ -4,8 +4,8 @@
using System;
using System.Collections.Generic;
-using NodeEditorFramework;
using NodeEditorFramework.Utilities;
+using NodeEditorFramework.IO;
using Object = UnityEngine.Object;
@@ -79,6 +79,7 @@ private static void setupBaseFramework ()
NodeTypes.FetchNodeTypes ();
NodeCanvasManager.FetchCanvasTypes ();
ConnectionPortManager.FetchNodeConnectionDeclarations ();
+ ImportExportManager.FetchIOFormats ();
// Setup Callback system
NodeEditorCallbacks.SetupReceivers ();
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs b/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs
new file mode 100644
index 00000000..9d90ee38
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs
@@ -0,0 +1,213 @@
+using System.IO;
+using UnityEngine;
+using NodeEditorFramework;
+using NodeEditorFramework.Utilities;
+
+namespace NodeEditorFramework.IO
+{
+ ///
+ /// Base class of an arbitrary Import/Export format based directly on the NodeCanvas
+ ///
+ public abstract class ImportExportFormat
+ {
+ ///
+ /// Identifier for this format, must be unique (e.g. 'XML')
+ ///
+ public abstract string FormatIdentifier { get; }
+
+ ///
+ /// Optional format description (e.g. 'Legacy', shown as 'XML (Legacy)')
+ ///
+ public virtual string FormatDescription { get { return ""; } }
+
+ ///
+ /// Optional extension for this format if saved as a file, e.g. 'xml', default equals FormatIdentifier
+ ///
+ public virtual string FormatExtension { get { return FormatIdentifier; } }
+
+ ///
+ /// Returns whether the location selection needs to be performed through a GUI (e.g. for a custom database access)
+ /// If true, the Import-/ExportLocationArgsGUI functions are called, else Import-/ExportLocationArgsSelection
+ ///
+ public virtual bool RequiresLocationGUI { get {
+#if UNITY_EDITOR
+ return false; // In the editor, use file browser seletion
+#else
+ return true; // At runtime, use GUI to select a file in a fixed folder
+#endif
+ }
+ }
+
+ ///
+ /// Folder for runtime IO operations relative to the game folder.
+ ///
+ public virtual string RuntimeIOPath { get { return "Files/NodeEditor/"; } }
+
+#if !UNITY_EDITOR
+ private string fileSelection = "";
+ private Rect fileSelectionMenuRect;
+#endif
+
+ ///
+ /// Called only if RequiresLocationGUI is true.
+ /// Displays GUI filling in locationArgs with the information necessary to locate the import operation.
+ /// Override along with RequiresLocationGUI for custom database access.
+ /// Return true or false to perform or cancel the import operation.
+ ///
+ public virtual bool? ImportLocationArgsGUI (ref object[] locationArgs)
+ {
+#if UNITY_EDITOR
+ return ImportLocationArgsSelection (out locationArgs);
+#else
+ GUILayout.Label("Import canvas from " + FormatIdentifier);
+ GUILayout.BeginHorizontal();
+ GUILayout.Label(RuntimeIOPath, GUILayout.ExpandWidth(false));
+ if (GUILayout.Button(string.IsNullOrEmpty(fileSelection)? "Select..." : fileSelection + "." + FormatExtension, GUILayout.ExpandWidth(true)))
+ {
+ // Find save files
+ DirectoryInfo dir = Directory.CreateDirectory(RuntimeIOPath);
+ FileInfo[] files = dir.GetFiles("*." + FormatExtension);
+ // Fill save file selection menu
+ GenericMenu fileSelectionMenu = new GenericMenu(false);
+ foreach (FileInfo file in files)
+ fileSelectionMenu.AddItem(new GUIContent(file.Name), false, () => fileSelection = Path.GetFileNameWithoutExtension(file.Name));
+ fileSelectionMenu.DropDown(fileSelectionMenuRect);
+ }
+ if (Event.current.type == EventType.Repaint)
+ {
+ Rect popupPos = GUILayoutUtility.GetLastRect();
+ fileSelectionMenuRect = new Rect(popupPos.x + 2, popupPos.yMax + 2, popupPos.width - 4, 0);
+ }
+ GUILayout.EndHorizontal();
+
+ // Finish operation buttons
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Cancel"))
+ return false;
+ if (GUILayout.Button("Import"))
+ {
+ if (string.IsNullOrEmpty(fileSelection) || !File.Exists(RuntimeIOPath + fileSelection + "." + FormatExtension))
+ return false;
+ fileSelection = Path.GetFileNameWithoutExtension(fileSelection);
+ locationArgs = new object[] { RuntimeIOPath + fileSelection + "." + FormatExtension };
+ return true;
+ }
+ GUILayout.EndHorizontal();
+
+ return null;
+#endif
+ }
+
+ ///
+ /// Called only if RequiresLocationGUI is false.
+ /// Returns the information necessary to locate the import operation.
+ /// By default it lets the user select a path as string[1].
+ ///
+ public virtual bool ImportLocationArgsSelection (out object[] locationArgs)
+ {
+ string path = null;
+#if UNITY_EDITOR
+ path = UnityEditor.EditorUtility.OpenFilePanel(
+ "Import " + FormatIdentifier + (!string.IsNullOrEmpty (FormatDescription)? (" (" + FormatDescription + ")") : ""),
+ "Assets", FormatExtension.ToLower ());
+#endif
+ locationArgs = new object[] { path };
+ return !string.IsNullOrEmpty (path);
+ }
+
+ ///
+ /// Called only if RequiresLocationGUI is true.
+ /// Displays GUI filling in locationArgs with the information necessary to locate the export operation.
+ /// Override along with RequiresLocationGUI for custom database access.
+ /// Return true or false to perform or cancel the export operation.
+ ///
+ public virtual bool? ExportLocationArgsGUI (string canvasName, ref object[] locationArgs)
+ {
+#if UNITY_EDITOR
+ return ExportLocationArgsSelection(canvasName, out locationArgs);
+#else
+ GUILayout.Label("Export canvas to " + FormatIdentifier);
+
+ // File save field
+ GUILayout.BeginHorizontal();
+ GUILayout.Label(RuntimeIOPath, GUILayout.ExpandWidth(false));
+ fileSelection = GUILayout.TextField(fileSelection, GUILayout.ExpandWidth(true));
+ GUILayout.Label("." + FormatExtension, GUILayout.ExpandWidth (false));
+ GUILayout.EndHorizontal();
+
+ // Finish operation buttons
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Cancel"))
+ return false;
+ if (GUILayout.Button("Export"))
+ {
+ if (string.IsNullOrEmpty(fileSelection))
+ return false;
+ fileSelection = Path.GetFileNameWithoutExtension(fileSelection);
+ locationArgs = new object[] { RuntimeIOPath + fileSelection + "." + FormatExtension };
+ return true;
+ }
+ GUILayout.EndHorizontal();
+
+ return null;
+#endif
+ }
+
+ ///
+ /// Called only if RequiresLocationGUI is false.
+ /// Returns the information necessary to locate the export operation.
+ /// By default it lets the user select a path as string[1].
+ ///
+ public virtual bool ExportLocationArgsSelection (string canvasName, out object[] locationArgs)
+ {
+ string path = null;
+#if UNITY_EDITOR
+ path = UnityEditor.EditorUtility.SaveFilePanel(
+ "Export " + FormatIdentifier + (!string.IsNullOrEmpty (FormatDescription)? (" (" + FormatDescription + ")") : ""),
+ "Assets", canvasName, FormatExtension.ToLower ());
+#endif
+ locationArgs = new object[] { path };
+ return !string.IsNullOrEmpty (path);
+ }
+
+ ///
+ /// Imports the canvas at the location specified in the args (usually string[1] containing the path) and returns it's simplified canvas data
+ ///
+ public abstract NodeCanvas Import (params object[] locationArgs);
+
+ ///
+ /// Exports the given simplified canvas data to the location specified in the args (usually string[1] containing the path)
+ ///
+ public abstract void Export (NodeCanvas canvas, params object[] locationArgs);
+ }
+
+ ///
+ /// Base class of an arbitrary Import/Export format based on a simple structural data best for most formats
+ ///
+ public abstract class StructuredImportExportFormat : ImportExportFormat
+ {
+ public override NodeCanvas Import (params object[] locationArgs)
+ {
+ CanvasData data = ImportData (locationArgs);
+ if (data == null)
+ return null;
+ return ImportExportManager.ConvertToNodeCanvas (data);
+ }
+
+ public override void Export (NodeCanvas canvas, params object[] locationArgs)
+ {
+ CanvasData data = ImportExportManager.ConvertToCanvasData (canvas);
+ ExportData (data, locationArgs);
+ }
+
+ ///
+ /// Imports the canvas at the location specified in the args (usually string[1] containing the path) and returns it's simplified canvas data
+ ///
+ public abstract CanvasData ImportData (params object[] locationArgs);
+
+ ///
+ /// Exports the given simplified canvas data to the location specified in the args (usually string[1] containing the path)
+ ///
+ public abstract void ExportData (CanvasData data, params object[] locationArgs);
+ }
+}
\ No newline at end of file
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs.meta b/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs.meta
new file mode 100644
index 00000000..82d98f77
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportFormat.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 961d85cbdc1829442907cdcfd40aaf01
+timeCreated: 1497884522
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportManager.cs b/Node_Editor/Framework/SaveSystem/ImportExportManager.cs
new file mode 100644
index 00000000..305dad25
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportManager.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+
+using NodeEditorFramework;
+using NodeEditorFramework.Utilities;
+
+namespace NodeEditorFramework.IO
+{
+ ///
+ /// Manager for Import Export operations, including fetching of all supported formats
+ ///
+ public static class ImportExportManager
+ {
+ private static Dictionary IOFormats;
+
+ private static Action _importMenuCallback;
+ private static Action _exportMenuCallback;
+
+ ///
+ /// Fetches every IO Format in the script assemblies to provide the framework with custom import and export formats
+ ///
+ public static void FetchIOFormats()
+ {
+ IOFormats = new Dictionary();
+ foreach (Type type in ReflectionUtility.getSubTypes (typeof(ImportExportFormat)))
+ {
+ ImportExportFormat formatter = (ImportExportFormat)Activator.CreateInstance (type);
+ IOFormats.Add (formatter.FormatIdentifier, formatter);
+ }
+ }
+
+ ///
+ /// Returns the format specified by the given IO format identifier
+ ///
+ public static ImportExportFormat ParseFormat (string IOformat)
+ {
+ ImportExportFormat formatter;
+ if (IOFormats.TryGetValue (IOformat, out formatter))
+ return formatter;
+ else
+ throw new ArgumentException ("Unknown format '" + IOformat + "'");
+ }
+
+ ///
+ /// Imports the canvas with the given formatter from the location specified in the args and returns it
+ ///
+ public static NodeCanvas ImportCanvas (ImportExportFormat formatter, params object[] args)
+ {
+ return formatter.Import (args);
+ }
+
+ ///
+ /// Exports the given canvas with the given formatter to the location specified in the args
+ ///
+ public static void ExportCanvas (NodeCanvas canvas, ImportExportFormat formatter, params object[] args)
+ {
+ formatter.Export (canvas, args);
+ }
+
+ #region Canvas Type Menu
+
+ public static void FillImportFormatMenu(ref GenericMenu menu, Action IOFormatSelection, string path = "")
+ {
+ _importMenuCallback = IOFormatSelection;
+ foreach (string formatID in IOFormats.Keys)
+ menu.AddItem(new GUIContent(path + formatID), false, unwrapInputFormatIDCallback, (object)formatID);
+ }
+
+ #if UNITY_EDITOR
+ public static void FillImportFormatMenu(ref UnityEditor.GenericMenu menu, Action IOFormatSelection, string path = "")
+ {
+ _importMenuCallback = IOFormatSelection;
+ foreach (string formatID in IOFormats.Keys)
+ menu.AddItem(new GUIContent(path + formatID), false, unwrapInputFormatIDCallback, (object)formatID);
+ }
+ #endif
+
+ public static void FillExportFormatMenu(ref GenericMenu menu, Action IOFormatSelection, string path = "")
+ {
+ _exportMenuCallback = IOFormatSelection;
+ foreach (string formatID in IOFormats.Keys)
+ menu.AddItem(new GUIContent(path + formatID), false, unwrapExportFormatIDCallback, (object)formatID);
+ }
+
+ #if UNITY_EDITOR
+ public static void FillExportFormatMenu(ref UnityEditor.GenericMenu menu, Action IOFormatSelection, string path = "")
+ {
+ _exportMenuCallback = IOFormatSelection;
+ foreach (string formatID in IOFormats.Keys)
+ menu.AddItem(new GUIContent(path + formatID), false, unwrapExportFormatIDCallback, (object)formatID);
+ }
+ #endif
+
+ private static void unwrapInputFormatIDCallback(object formatID)
+ {
+ _importMenuCallback((string)formatID);
+ }
+
+ private static void unwrapExportFormatIDCallback(object formatID)
+ {
+ _exportMenuCallback((string)formatID);
+ }
+
+ #endregion
+
+ #region Converter
+
+ ///
+ /// Converts the NodeCanvas to a simplified CanvasData
+ ///
+ internal static CanvasData ConvertToCanvasData (NodeCanvas canvas)
+ {
+ if (canvas == null)
+ return null;
+
+ // Validate canvas and create canvas data for it
+ canvas.Validate ();
+ CanvasData canvasData = new CanvasData (canvas);
+
+ // Store Lookup-Table for all ports
+ Dictionary portDatas = new Dictionary();
+
+ foreach (Node node in canvas.nodes)
+ {
+ // Create node data
+ NodeData nodeData = new NodeData (node);
+ canvasData.nodes.Add (nodeData.nodeID, nodeData);
+
+ foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(node))
+ { // Fetch all connection ports and record them
+ ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(node);
+ PortData portData = new PortData(nodeData, port, portDecl.portField.Name);
+ nodeData.connectionPorts.Add(portData);
+ portDatas.Add(port, portData);
+ }
+
+ // Fetch all serialized node variables specific to each node's implementation
+ FieldInfo[] serializedFields = ReflectionUtility.getSerializedFields (node.GetType (), typeof(Node));
+ foreach (FieldInfo field in serializedFields)
+ { // Create variable data and enter the
+ if (field.FieldType.IsSubclassOf(typeof(ConnectionPort)))
+ continue;
+ VariableData varData = new VariableData (field);
+ nodeData.variables.Add (varData);
+ object varValue = field.GetValue (node);
+ if (field.FieldType.IsValueType) // Store value of the object
+ varData.value = varValue;
+ else // Store reference to the object
+ varData.refObject = canvasData.ReferenceObject (varValue);
+ }
+ }
+
+ foreach (PortData portData in portDatas.Values)
+ { // Record the connections of this port
+ foreach (ConnectionPort conPort in portData.port.connections)
+ {
+ PortData conPortData; // Get portData associated with the connection port
+ if (portDatas.TryGetValue(conPort, out conPortData))
+ canvasData.RecordConnection(portData, conPortData);
+ }
+ }
+
+ canvasData.editorStates = new EditorStateData[canvas.editorStates.Length];
+ for (int i = 0; i < canvas.editorStates.Length; i++)
+ { // Record all editorStates
+ NodeEditorState state = canvas.editorStates[i];
+ NodeData selected = state.selectedNode == null ? null : canvasData.FindNode(state.selectedNode);
+ canvasData.editorStates[i] = new EditorStateData(selected, state.panOffset, state.zoom);
+ }
+
+ return canvasData;
+ }
+
+ ///
+ /// Converts the simplified CanvasData back to a proper NodeCanvas
+ ///
+ internal static NodeCanvas ConvertToNodeCanvas (CanvasData canvasData)
+ {
+ if (canvasData == null)
+ return null;
+ NodeCanvas nodeCanvas = NodeCanvas.CreateCanvas(canvasData.type);
+ nodeCanvas.name = nodeCanvas.saveName = canvasData.name;
+ nodeCanvas.nodes.Clear();
+ NodeEditor.BeginEditingCanvas(nodeCanvas);
+
+ foreach (NodeData nodeData in canvasData.nodes.Values)
+ { // Read all nodes
+ Node node = Node.Create (nodeData.typeID, nodeData.nodePos, null, true);
+ if (node == null)
+ continue;
+ foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(node))
+ { // Find stored ports for each node port declaration
+ PortData portData = nodeData.connectionPorts.Find((PortData data) => data.varName == portDecl.portField.Name);
+ if (portData != null) // Stored port has been found, record
+ portData.port = (ConnectionPort)portDecl.portField.GetValue(node);
+ }
+ foreach (VariableData varData in nodeData.variables)
+ { // Restore stored variable to node
+ FieldInfo field = node.GetType().GetField(varData.name);
+ if (field != null)
+ field.SetValue(node, varData.refObject != null ? varData.refObject.data : varData.value);
+ }
+ }
+
+ foreach (ConnectionData conData in canvasData.connections)
+ { // Restore all connections
+ if (conData.port1.port == null || conData.port2.port == null)
+ { // Not all ports where saved in canvasData
+ Debug.Log("Incomplete connection " + conData.port1.varName + " and " + conData.port2.varName + "!");
+ continue;
+ }
+ conData.port1.port.TryApplyConnection(conData.port2.port, true);
+ }
+
+ nodeCanvas.editorStates = new NodeEditorState[canvasData.editorStates.Length];
+ for (int i = 0; i < canvasData.editorStates.Length; i++)
+ { // Read all editorStates
+ EditorStateData stateData = canvasData.editorStates[i];
+ NodeEditorState state = ScriptableObject.CreateInstance();
+ state.selectedNode = stateData.selectedNode == null ? null : canvasData.FindNode(stateData.selectedNode.nodeID).node;
+ state.panOffset = stateData.panOffset;
+ state.zoom = stateData.zoom;
+ state.canvas = nodeCanvas;
+ state.name = "EditorState";
+ nodeCanvas.editorStates[i] = state;
+ }
+
+ NodeEditor.EndEditingCanvas();
+ return nodeCanvas;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportManager.cs.meta b/Node_Editor/Framework/SaveSystem/ImportExportManager.cs.meta
new file mode 100644
index 00000000..775a826f
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportManager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7761e7c3d61739f4bbb8b023e3e91adc
+timeCreated: 1497983354
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs b/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs
new file mode 100644
index 00000000..6b079ef5
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using UnityEngine;
+
+namespace NodeEditorFramework.IO
+{
+ public class CanvasData
+ {
+ public NodeCanvas canvas;
+
+ public string name;
+ public Type type;
+
+ public EditorStateData[] editorStates;
+
+ public Dictionary nodes = new Dictionary();
+ public List connections = new List();
+ public Dictionary objects = new Dictionary();
+
+ public CanvasData(NodeCanvas nodeCanvas)
+ {
+ canvas = nodeCanvas;
+ name = nodeCanvas.name;
+ type = nodeCanvas.GetType();
+ }
+
+ public CanvasData(Type canvasType, string canvasName)
+ {
+ type = canvasType;
+ name = canvasName;
+ }
+
+ public ObjectData ReferenceObject(object obj)
+ {
+ foreach (ObjectData data in objects.Values)
+ {
+ if (data.data == obj)
+ return data;
+ }
+ ObjectData objData = new ObjectData(obj);
+ objects.Add(objData.refID, objData);
+ return objData;
+ }
+
+ public ObjectData FindObject(int refID)
+ {
+ ObjectData data;
+ objects.TryGetValue(refID, out data);
+ return data;
+ }
+
+ public NodeData FindNode(Node node)
+ {
+ foreach (NodeData data in nodes.Values)
+ {
+ if (data.node == node)
+ return data;
+ }
+ return null;
+ }
+
+ public NodeData FindNode(int nodeID)
+ {
+ NodeData data;
+ nodes.TryGetValue(nodeID, out data);
+ return data;
+ }
+
+ public bool RecordConnection(PortData portData1, PortData portData2)
+ {
+ if (!portData1.connections.Contains(portData2))
+ portData1.connections.Add(portData2);
+ if (!portData2.connections.Contains(portData1))
+ portData2.connections.Add(portData1);
+ if (!connections.Exists((ConnectionData conData) => conData.isPart(portData1) && conData.isPart(portData2)))
+ { // Connection hasn't already been recorded
+ ConnectionData conData = new ConnectionData(portData1, portData2);
+ connections.Add(conData);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public class EditorStateData
+ {
+ public NodeData selectedNode;
+ public Vector2 panOffset;
+ public float zoom;
+
+ public EditorStateData(NodeData SelectedNode, Vector2 PanOffset, float Zoom)
+ {
+ selectedNode = SelectedNode;
+ panOffset = PanOffset;
+ zoom = Zoom;
+ }
+ }
+
+ public class NodeData
+ {
+ public Node node;
+
+ public int nodeID;
+ public string typeID;
+ public Vector2 nodePos;
+
+ public List connectionPorts = new List();
+ public List variables = new List();
+
+ public NodeData(Node n)
+ {
+ node = n;
+ typeID = node.GetID;
+ nodeID = node.GetHashCode();
+ nodePos = node.rect.position;
+ }
+
+ public NodeData(string TypeID, int NodeID, Vector2 Pos)
+ {
+ typeID = TypeID;
+ nodeID = NodeID;
+ nodePos = Pos;
+ }
+ }
+
+ public class PortData
+ {
+ public ConnectionPort port;
+
+ public int portID;
+ public NodeData body;
+ public string varName;
+
+ public List connections = new List();
+
+ public PortData(NodeData Body, ConnectionPort Port, string VarName)
+ {
+ port = Port;
+ portID = port.GetHashCode();
+ body = Body;
+ varName = VarName;
+ }
+
+ public PortData(NodeData Body, string VarName, int PortID)
+ {
+ portID = PortID;
+ body = Body;
+ varName = VarName;
+ }
+ }
+
+ public class ConnectionData
+ {
+ public PortData port1;
+ public PortData port2;
+
+ public ConnectionData(PortData Port1, PortData Port2)
+ {
+ port1 = Port1;
+ port2 = Port2;
+ }
+
+ public bool isPart (PortData port)
+ {
+ return port1 == port || port2 == port;
+ }
+ }
+
+ public class VariableData
+ {
+ public string name;
+ public ObjectData refObject;
+ public object value;
+
+ public VariableData(FieldInfo field)
+ {
+ name = field.Name;
+ }
+
+ public VariableData(string fieldName)
+ {
+ name = fieldName;
+ }
+ }
+
+ public class ObjectData
+ {
+ public int refID;
+ public Type type;
+ public object data;
+
+ public ObjectData(object obj)
+ {
+ refID = obj.GetHashCode();
+ type = obj.GetType();
+ data = obj;
+ }
+
+ public ObjectData(int objRefID, object obj)
+ {
+ refID = objRefID;
+ type = obj.GetType();
+ data = obj;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs.meta b/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs.meta
new file mode 100644
index 00000000..36e2df85
--- /dev/null
+++ b/Node_Editor/Framework/SaveSystem/ImportExportStructure.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 84af943011bc7e6448cd010e393c6c02
+timeCreated: 1498754587
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Node_Editor/Framework/SaveSystem/NodeEditorSaveManager.cs b/Node_Editor/Framework/SaveSystem/NodeEditorSaveManager.cs
index e57de749..1f19960b 100644
--- a/Node_Editor/Framework/SaveSystem/NodeEditorSaveManager.cs
+++ b/Node_Editor/Framework/SaveSystem/NodeEditorSaveManager.cs
@@ -534,15 +534,16 @@ private static T ReplaceSO (List scriptableObjects, List
- /// Returns the editorState with the specified name in canvas. If not found it will create a new one with that name.
+ /// Returns the editorState with the specified name in canvas.
+ /// If not found but others and forceFind is false, a different one is chosen randomly, else a new one is created.
///
- public static NodeEditorState ExtractEditorState (NodeCanvas canvas, string stateName)
+ public static NodeEditorState ExtractEditorState (NodeCanvas canvas, string stateName, bool forceFind = false)
{
NodeEditorState state = null;
- if (canvas.editorStates.Length > 0)
- { // Search for the editorState
- state = canvas.editorStates.First ((NodeEditorState s) => s != null && s.name == stateName);
- }
+ if (canvas.editorStates.Length > 0) // Search for the editorState
+ state = canvas.editorStates.FirstOrDefault ((NodeEditorState s) => s.name == stateName);
+ if (state == null && !forceFind) // Take any other if not found
+ state = canvas.editorStates.FirstOrDefault();
if (state == null)
{ // Create editorState
state = ScriptableObject.CreateInstance();