using System; using System.Linq; using System.Text.RegularExpressions; using System.Collections.Generic; using UnityEngine; namespace XNode { /// Base class for all node graphs [Serializable] public abstract class NodeGraph : ScriptableObject { /// All nodes in the graph. /// See: [SerializeField] public List nodes = new List(); /// All variables in the graph. [SerializeField] public List variables = new List(); /// Add a node to the graph by type public T AddNode() where T : Node { return AddNode(typeof(T)) as T; } /// Add a node to the graph by type public virtual Node AddNode(Type type) { Node node = ScriptableObject.CreateInstance(type) as Node; #if UNITY_EDITOR if (!Application.isPlaying) { UnityEditor.AssetDatabase.AddObjectToAsset(node, this); UnityEditor.AssetDatabase.SaveAssets(); node.name = UnityEditor.ObjectNames.NicifyVariableName(node.name); } #endif nodes.Add(node); node.graph = this; node.Init(); return node; } /// Creates a copy of the original node in the graph public virtual Node CopyNode(Node original) { Node node = ScriptableObject.Instantiate(original); node.ClearConnections(); #if UNITY_EDITOR if (!Application.isPlaying) { UnityEditor.AssetDatabase.AddObjectToAsset(node, this); UnityEditor.AssetDatabase.SaveAssets(); node.name = UnityEditor.ObjectNames.NicifyVariableName(node.name); } #endif nodes.Add(node); node.graph = this; node.Init(); return node; } /// Safely remove a node and all its connections /// public void RemoveNode(Node node) { node.ClearConnections(); #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(node, true); UnityEditor.AssetDatabase.SaveAssets(); } #endif nodes.Remove(node); } /// Remove all nodes, connections and variables from the graph public void Clear() { nodes.Clear(); variables.Clear(); } /// Create a new deep copy of this graph public XNode.NodeGraph Copy() { // Instantiate a new nodegraph instance NodeGraph graph = Instantiate(this); // Instantiate all nodes inside the graph for (int i = 0; i < nodes.Count; i++) { Node node = Instantiate(nodes[i]) as Node; node.graph = graph; graph.nodes[i] = node; node.Init(); } // Redirect all connections for (int i = 0; i < graph.nodes.Count; i++) { foreach (NodePort port in graph.nodes[i].Ports) { port.Redirect(nodes, graph.nodes); } } return graph; } /// Add a variable to the graph /// the new variable data /// the actual id used, avoiding duplicates public string AddVariable(Variable newVariable) { newVariable.id = GetSafeId(newVariable.id); newVariable.typeString = GetSafeType(newVariable.typeString); variables.Add(newVariable); return newVariable.id; } /// Remove a variable from the graph public void RemoveVariable(string id) { variables.Remove(GetVariable(id)); } /// Get a variable from the graph public Variable GetVariable(string id) { return variables.Find((Variable v) => v.id == id); } /// Get a duplication safe id public string GetSafeId(string id) { id = id.ToLower(); id.Trim(); var rx = new Regex(@"\s"); rx.Replace(id, new MatchEvaluator((Match m) => "_")); var existingVariable = variables.Find((Variable v) => v.id == id); if (existingVariable == null) return id; int index = 1; while (variables.Find((Variable v) => v.id == id + "_" + index.ToString()) != null) index++; return id + "_" + index.ToString(); } /// Get a variable safe type public static string GetSafeType(string type) { const string UE = "UnityEngine."; if (type.Contains(UE)) { type = type.Substring(UE.Length); type = type.Substring(0,1).ToLower() + type.Substring(1); } return type; } } }