diff --git a/Samples/New HelloGraph.asset b/Samples/New HelloGraph.asset index bf5255f..cc4a8b8 100644 --- a/Samples/New HelloGraph.asset +++ b/Samples/New HelloGraph.asset @@ -12,15 +12,74 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 2051a0adbd1ba974084a535dd06ab7d7, type: 3} m_Name: New HelloGraph m_EditorClassIdentifier: - nodeList: [] - nodeLinks: [] + nodeList: + - id: 0 + - id: 1 + nodeLinks: + - inPort: + portEntryName: A + nodeDataId: 94327133-34e1-47bb-a365-0782120c581e + outPort: + portEntryName: OutputList:1 + nodeDataId: 8289b5d6-f55f-480e-8c16-ae3af7a282dd blackboardData: - id: 0 + id: 2 sceneReference: editorModels: [] graphViewModel: - id: 0 + id: 3 references: version: 1 00000000: - type: {class: , ns: , asm: } + type: {class: AddNode, ns: Samples, asm: Assembly-CSharp} + data: + positionInView: + serializedVersion: 2 + x: 396 + y: 215 + width: 0 + height: 0 + id: 8289b5d6-f55f-480e-8c16-ae3af7a282dd + nodeName: AddNode + entryPoint: 0 + isTest: 0 + 00000001: + type: {class: AddNode, ns: Samples, asm: Assembly-CSharp} + data: + positionInView: + serializedVersion: 2 + x: 569 + y: 215 + width: 0 + height: 0 + id: 94327133-34e1-47bb-a365-0782120c581e + nodeName: AddNode + entryPoint: 0 + isTest: 0 + 00000002: + type: {class: HelloBlackboard, ns: TNode.Samples, asm: Assembly-CSharp} + data: + positionInView: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + id: + HelloString: + HelloGameObject: {fileID: 0} + V3S: [] + V2S: [] + 00000003: + type: {class: GraphViewModel, ns: TNode.TNodeCore.Editor.Models, asm: Taoria.TNodeCore.Runtime} + data: + positionInView: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + id: + persistScale: 1 + persistOffset: {x: 0, y: 0} + isBlackboardOn: 1 diff --git a/TNodeCore/Editor/GraphEditor.cs b/TNodeCore/Editor/GraphEditor.cs index c19e081..98306b8 100644 --- a/TNodeCore/Editor/GraphEditor.cs +++ b/TNodeCore/Editor/GraphEditor.cs @@ -11,17 +11,17 @@ namespace TNodeCore.Editor{ // public class SelectGraphWindow : EditorWindow{ // public EditorWindow parent; // public Type graphType; - // public static void ShowWindow (GraphEditor parent) where T:GraphData{ + // public static void ShowWindow (GraphEditor parent) where type:GraphData{ // var window = GetWindow(); - // window.graphType = typeof(T); + // window.graphType = typeof(type); // window.Show(); // window.parent = parent; // } // private void OnGUI(){ // - // if(GUILayout.Button("Create An Graph")){ + // if(GUILayout.Button("CreateProp An Graph")){ // //Add a save file dialog to save the graph - // //Create the graph + // //CreateProp the graph // var graphAsset = ScriptableObject.CreateInstance(graphType); // var path = EditorUtility.SaveFilePanel("Save Graph", "", "", "asset"); // //Save the graph @@ -116,7 +116,7 @@ namespace TNodeCore.Editor{ { string path = EditorUtility.SaveFilePanel("Save Graph", "", "", "asset"); if (path.Length != 0){ - //Create a new asset file with type of GraphDataType + //CreateProp a new asset file with type of GraphDataType T asset = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(asset, path); AssetDatabase.SaveAssets(); diff --git a/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs b/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs index d36cf64..1540f7c 100644 --- a/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs +++ b/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs @@ -42,7 +42,7 @@ namespace TNodeCore.Editor.NodeGraphView{ public bool AutoUpdate{ get; set; } /// - /// Create the blackboard view for the graph view. + /// CreateProp the blackboard view for the graph view. /// public void CreateBlackboard(); /// diff --git a/TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs b/TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs index e6531a7..be2baee 100644 --- a/TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs +++ b/TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs @@ -5,7 +5,7 @@ using UnityEditor; using UnityEngine; using UnityEngine.UIElements; -//add an attribute right click asset panel and select "TNodeCore/Create/Create New Graph Editor" to call this editor +//add an attribute right click asset panel and select "TNodeCore/CreateProp/CreateProp New Graph Editor" to call this editor namespace TNodeCore.Editor.Tools.GraphEditorCreator{ @@ -19,8 +19,8 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{ private TextField _graphClassNameTextField; private Button _createButton; private readonly SourceGeneratorForGraphEditor _sourceGeneratorForGraphEditor = new SourceGeneratorForGraphEditor(); - [MenuItem("Assets/Create/TNodeCore/Create New Graph Editor")] - [MenuItem("TNodeCore/Create New Graph Editor")] + [MenuItem("Assets/CreateProp/TNodeCore/CreateProp New Graph Editor")] + [MenuItem("TNodeCore/CreateProp New Graph Editor")] public static void ShowExample() { GraphEditorCreator wnd = GetWindow(); @@ -46,7 +46,7 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{ VisualElement labelFromUXML = m_VisualTreeAsset.Instantiate(); root.Add(labelFromUXML); - //Register a callback when Create Button is clicked + //Register a callback when CreateProp Button is clicked _createButton = root.Q [NonSerialized] private bool _build = false; - + + [NonSerialized] private IEnumerator _runtimeNodeEnumerator; + /// /// Build the graph tool and other dependencies for the runtime graph /// + public virtual void Build(){ if (_build) return; @@ -58,7 +62,7 @@ namespace TNodeCore.Runtime.Components{ CreateRuntimeNodeIfNone(nodeData); } var nodeList = RuntimeNodes.Values; - _graphTool = new GraphTool(nodeList.ToList(),RuntimeNodes,this); + _graphTool = new GraphTool(this); var sceneNodes = RuntimeNodes.Values.Where(x => x.NodeData is SceneNode).Select(x => x.NodeData as SceneNode); foreach (var sceneNode in sceneNodes){ if (sceneNode != null) sceneNode.BlackboardData = runtimeBlackboardData; @@ -66,6 +70,7 @@ namespace TNodeCore.Runtime.Components{ #if UNITY_EDITOR BuildSceneNode(); #endif + _runtimeNodeEnumerator = _graphTool.BreathFirstSearch(); _build = true; } @@ -80,34 +85,13 @@ namespace TNodeCore.Runtime.Components{ /// /// Node data you provided /// - public RuntimeNode Get(NodeData nodeData){ - if(!_build) - Build(); - if(RuntimeNodes.ContainsKey(nodeData.id)){ - return RuntimeNodes[nodeData.id]; - } - return null; - } - /// - /// Get the runtime node from an id - /// - /// - /// - public RuntimeNode Get(string id){ - if(!_build) - Build(); - if (RuntimeNodes.ContainsKey(id)){ - return RuntimeNodes[id]; - } - return null; - } //DFS search to run a node. public bool RunOnDependency(NodeData startNode){ if(!_build) Build(); if (_graphTool == null) return false; - _graphTool.RunNodeDependently(Get(startNode)); + _graphTool.RunNodeDependently(GetRuntimeNode(startNode)); return true; } public bool TraverseAll(){ @@ -180,12 +164,29 @@ namespace TNodeCore.Runtime.Components{ var runtimeInNode = new RuntimeNode(inNode); RuntimeNodes.Add(inNode.id,runtimeInNode); } - RuntimeNodes[inNode.id].InputLink.Add(linkData); - + RuntimeNodes[inNode.id].InputLinks.Add(linkData); } public List GetRuntimeNodesOfType(){ return RuntimeNodes.Values.Where(x => typeof(T).IsAssignableFrom(x.NodeType)).ToList(); } + + public void ResetState(){ + _runtimeNodeEnumerator = _graphTool.BreathFirstSearch(); + } + + public NodeData CurrentNode(){ + return _runtimeNodeEnumerator.Current?.NodeData; + } + + public RuntimeNode MoveNext(){ + _runtimeNodeEnumerator.MoveNext(); + return _runtimeNodeEnumerator.Current; + } + + public RuntimeNode CurrentRuntimeNode(){ + return _runtimeNodeEnumerator.Current; + } + public List GetRuntimeNodesOfType(Type type){ return RuntimeNodes.Values.Where(x => type.IsAssignableFrom(type)).ToList(); } @@ -236,7 +237,7 @@ namespace TNodeCore.Runtime.Components{ var runtimeOutNode = new RuntimeNode(outNode); RuntimeNodes.Add(outNode.id,runtimeOutNode); } - RuntimeNodes[outNode.id].OutputLink.Add(linkData); + RuntimeNodes[outNode.id].OutputLinks.Add(linkData); } public void OnValidate(){ @@ -263,7 +264,42 @@ namespace TNodeCore.Runtime.Components{ public virtual void RuntimeExecute(){ _graphTool.DirectlyTraversal(); } - + + public RuntimeNode GetRuntimeNode(NodeData nodeData){ + if(!_build) + Build(); + if(RuntimeNodes.ContainsKey(nodeData.id)){ + return RuntimeNodes[nodeData.id]; + } + return null; + } + + public RuntimeNode GetRuntimeNode(string id){ + if(!_build) + Build(); + if(RuntimeNodes.ContainsKey(id)){ + return RuntimeNodes[id]; + } + return null; + } + + public List GetRuntimeNodes(){ + return RuntimeNodes.Values.ToList(); + } + + public Dictionary GetRuntimeNodesDictionary(){ + return RuntimeNodes; + } + + + public NodeData GetNode(string id){ + if(!_build) + Build(); + if(RuntimeNodes.ContainsKey(id)){ + return RuntimeNodes[id].NodeData; + } + return null; + } } public class SceneDataPersistent:MonoBehaviour,ISerializationCallbackReceiver{ diff --git a/TNodeCore/Runtime/SceneSerializedData.cs b/TNodeCore/Runtime/Components/SceneSerializedData.cs similarity index 100% rename from TNodeCore/Runtime/SceneSerializedData.cs rename to TNodeCore/Runtime/Components/SceneSerializedData.cs diff --git a/TNodeCore/Runtime/SceneSerializedData.cs.meta b/TNodeCore/Runtime/Components/SceneSerializedData.cs.meta similarity index 100% rename from TNodeCore/Runtime/SceneSerializedData.cs.meta rename to TNodeCore/Runtime/Components/SceneSerializedData.cs.meta diff --git a/TNodeCore/Runtime/ConditionalRuntimeNode.cs b/TNodeCore/Runtime/ConditionalRuntimeNode.cs deleted file mode 100644 index 516975a..0000000 --- a/TNodeCore/Runtime/ConditionalRuntimeNode.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using TNode.TNodeCore.Runtime.Models; -using TNodeCore.Runtime.Models; -using UnityEngine; - -namespace TNodeCore.Runtime{ - public class ConditionalRuntimeNode:RuntimeNode{ - private readonly List>> PossibleTransition; - public ConditionalRuntimeNode(NodeData nodeData) : base(nodeData){ - if (nodeData is ConditionalNode conditionalNode){ - var transitionPort = GetPortsOfType(); - PossibleTransition = new List>>(); - foreach (var port in transitionPort){ - if(GetPortDirection(port)==Direction.Input) continue; - PossibleTransition.Add(new Tuple>(port,() => (bool)GetOutput(port)) ); - } - } - else{ - Debug.LogError("The Conditional runtime node must be applied on a Conditional node"); - } - } - - public string[] GetConditionalNextIds(){ - var ports = PossibleTransition.Where(x => x.Item2()); - var portNames = ports.Select(x => x.Item1); - //Search output links to found the link contains portNames as outport's name - var outputLinks = OutputLink.Where(x => portNames.Contains(x.outPort.portEntryName)); - return outputLinks.Select(x => x.inPort.nodeDataId).ToArray(); - } - - } -} \ No newline at end of file diff --git a/TNodeCore/Runtime/DataWrapper.cs b/TNodeCore/Runtime/Extensions/DataWrapper.cs similarity index 100% rename from TNodeCore/Runtime/DataWrapper.cs rename to TNodeCore/Runtime/Extensions/DataWrapper.cs diff --git a/TNodeCore/Runtime/DataWrapper.cs.meta b/TNodeCore/Runtime/Extensions/DataWrapper.cs.meta similarity index 100% rename from TNodeCore/Runtime/DataWrapper.cs.meta rename to TNodeCore/Runtime/Extensions/DataWrapper.cs.meta diff --git a/TNodeCore/Runtime/Models/ConditionalNode.cs b/TNodeCore/Runtime/Models/ConditionalNode.cs index 4fe96f4..21e8c82 100644 --- a/TNodeCore/Runtime/Models/ConditionalNode.cs +++ b/TNodeCore/Runtime/Models/ConditionalNode.cs @@ -1,9 +1,14 @@ -using TNodeCore.Runtime.Attributes.Ports; +using TNodeCore.Runtime; +using TNodeCore.Runtime.Attributes.Ports; using TNodeCore.Runtime.Models; namespace TNode.TNodeCore.Runtime.Models{ public class ConditionalNode:NodeData{ [Input] - public bool In{ get; set; } + public object In{ get; set; } + } + public struct TransitionCondition{ + public bool Condition; + public int Priority; } } \ No newline at end of file diff --git a/TNodeCore/Runtime/Models/NodeLink.cs b/TNodeCore/Runtime/Models/NodeLink.cs index b70f370..2f48e71 100644 --- a/TNodeCore/Runtime/Models/NodeLink.cs +++ b/TNodeCore/Runtime/Models/NodeLink.cs @@ -1,4 +1,5 @@ using System; +using UnityEngine.Serialization; namespace TNodeCore.Runtime.Models{ //NodeAttribute links are stored in output side of the two node port. @@ -8,10 +9,10 @@ namespace TNodeCore.Runtime.Models{ public PortInfo inPort; public PortInfo outPort; - public NodeLink(PortInfo inPort, PortInfo outPort){ - this.inPort = inPort; - this.outPort = outPort; - } + public NodeLink(PortInfo inPort, PortInfo outPort){ + this.inPort = inPort; + this.outPort = outPort; + } } } \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeCache/IModelPropertyAccessor.cs b/TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs similarity index 84% rename from TNodeCore/Runtime/RuntimeCache/IModelPropertyAccessor.cs rename to TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs index 65030a4..b69ecf8 100644 --- a/TNodeCore/Runtime/RuntimeCache/IModelPropertyAccessor.cs +++ b/TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs @@ -2,7 +2,7 @@ using UnityEditor.Experimental.GraphView; namespace TNodeCore.Runtime.RuntimeCache{ - public interface IModelPropertyAccessor{ + public interface IModelPortAccessor{ object GetValue(object model); void SetValue(object model, object value); diff --git a/TNodeCore/Runtime/RuntimeCache/IModelPropertyAccessor.cs.meta b/TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs.meta similarity index 100% rename from TNodeCore/Runtime/RuntimeCache/IModelPropertyAccessor.cs.meta rename to TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs.meta diff --git a/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs b/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs index 3e04081..27ed998 100644 --- a/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs +++ b/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs @@ -10,24 +10,41 @@ using TNodeCore.Runtime.Models; using UnityEngine; namespace TNodeCore.Runtime.RuntimeCache{ - public class PropAccessor:IModelPropertyAccessor{ + public class PortAccessor:IModelPortAccessor{ public readonly Func Get; public readonly Action Set; - public PropAccessor(string propName){ - Type t = typeof(T1); + public PortAccessor(string name,bool property){ + if (property){ + Type t = typeof(T1); - MethodInfo getter = t.GetMethod("get_" + propName); - MethodInfo setter = t.GetMethod("set_" + propName); - Type = getter?.ReturnType??setter?.GetParameters()[0].ParameterType; - if(getter!=null) - Get = (Func)Delegate.CreateDelegate(typeof(Func), null, getter); - if(setter!=null) - Set = (Action)Delegate.CreateDelegate(typeof(Action), null, setter); - } - public static PropAccessor Create(string propName){ - return new PropAccessor(propName); + MethodInfo getter = t.GetMethod("get_" + name); + MethodInfo setter = t.GetMethod("set_" + name); + Type = getter?.ReturnType??setter?.GetParameters()[0].ParameterType; + if(getter!=null) + Get = (Func)Delegate.CreateDelegate(typeof(Func), null, getter); + if(setter!=null) + Set = (Action)Delegate.CreateDelegate(typeof(Action), null, setter); + } + else{ + Type t = typeof(T1); + MethodInfo method = t.GetMethod(name); + if (method == null){ + throw new Exception("Method not found for name " + name); + } + if (method.ReturnType != typeof(void)){ + Type = method.ReturnType; + Get = (Func)Delegate.CreateDelegate(typeof(Func), null, method); + } + else{ + Type = method.GetParameters()[0].ParameterType; + Set = (Action)Delegate.CreateDelegate(typeof(Action), null, method); + } + } + } + + public object GetValue(object model){ return Get((T1)model); } @@ -38,39 +55,9 @@ namespace TNodeCore.Runtime.RuntimeCache{ public Type Type{ get; set; } } - public class MethodAccessorInput:IMethodAccessorInput{ - public readonly Action Set; - public MethodAccessorInput(string methodName){ - Type t = typeof(T1); - MethodInfo setter = t.GetMethod(methodName); - if (setter != null) - Set = (Action) Delegate.CreateDelegate(typeof(Action), null, setter); - } - - public void SetValue(object model,object value){ - Set((T1)model,(T2)value); - } - } - public class MethodAccessorOutput : IMethodAccessorOutput{ - public readonly Func Get; - - public MethodAccessorOutput(string methodName){ - Type t = typeof(T1); - MethodInfo getter = t.GetMethod(methodName); - if(getter!=null) - Get = (Func)Delegate.CreateDelegate(typeof(Func), null, getter); - - - } - public object GetValue(object model){ - return Get((T1)model); - } - } - public interface IMethodAccessorInput{ - public void SetValue(object model,object o); - } + internal class PortConverterHelper : IPortConverterHelper{ private readonly PortTypeConversion _converter; @@ -122,13 +109,9 @@ namespace TNodeCore.Runtime.RuntimeCache{ new Dictionary>(); public readonly Dictionary> CachedDelegatesForSettingValue = new Dictionary>(); - public readonly Dictionary> CachedPropertyAccessors = - new Dictionary> (); - public readonly Dictionary> InputMethodPorts = new Dictionary>(); - - public readonly Dictionary> OutputMethodPorts = - new Dictionary>(); - + public readonly Dictionary> CachedPortAccessors = + new Dictionary> (); + /// /// TODO: Converters now work globally, but it should be possible to specify a converter for a specific graph.but it will be too nested.so in current implementation, we will use a global converter. /// @@ -320,44 +303,35 @@ namespace TNodeCore.Runtime.RuntimeCache{ } } //TODO: CACHE IT AS FUNCTION - private static IModelPropertyAccessor CreatePropertyCache(string propName,Type targetType,Type valueType){ - var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType); - var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)}); - var instance = constructor?.Invoke(new object[]{propName}); - return (IModelPropertyAccessor) instance; - } - public static IMethodAccessorInput CreateMethodInputCache(string methodName,Type targetType,Type inputTypes){ - var makeGenericType = typeof (MethodAccessorInput<,>).MakeGenericType(targetType,inputTypes); - var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)}); - var instance = constructor?.Invoke(new object[]{methodName}); - return (IMethodAccessorInput) instance; - } - public static IMethodAccessorOutput CreateMethodOutputCache(string methodName,Type targetType,Type outputTypes){ - var makeGenericType = typeof (MethodAccessorOutput<,>).MakeGenericType(targetType,outputTypes); - var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)}); - var instance = constructor?.Invoke(new object[]{methodName}); - return (IMethodAccessorOutput) instance; + private static IModelPortAccessor CreatePortCacheForProperty(string propName,Type targetType,Type valueType){ + var makeGenericType = typeof (PortAccessor<,>).MakeGenericType(targetType,valueType); + var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string),typeof(bool)}); + var instance = constructor?.Invoke(new object[]{propName,true}); + return (IModelPortAccessor) instance; + } + private static IModelPortAccessor CreatePortCacheForMethod(string methodName,Type targetType,Type valueType){ + var makeGenericType = typeof (PortAccessor<,>).MakeGenericType(targetType,valueType); + var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string),typeof(bool)}); + var instance = constructor?.Invoke(new object[]{methodName,false}); + return (IModelPortAccessor) instance; } public void CacheRuntimeNodeData(Type type){ if (type == null) return; if(!CachedDelegatesForGettingValue.ContainsKey(type)){ CachedDelegatesForGettingValue.Add(type, new Dictionary()); CachedDelegatesForSettingValue.Add(type,new Dictionary()); - CachedPropertyAccessors.Add(type,new Dictionary()); - InputMethodPorts.Add(type,new Dictionary()); - OutputMethodPorts.Add(type,new Dictionary()); - - + CachedPortAccessors.Add(type,new Dictionary()); var properties = type.GetProperties(); foreach(var property in properties){ - - var propertyAccessor = CreatePropertyCache(property.Name,type,property.PropertyType); - CachedPropertyAccessors[type].Add(property.Name,propertyAccessor); + var portAttribute = property.GetCustomAttribute(); + if (portAttribute == null) continue; + var propertyAccessor = CreatePortCacheForProperty(property.Name,type,property.PropertyType); + CachedPortAccessors[type].Add(property.Name,propertyAccessor); } //register the fields var fields = type.GetFields(); foreach(var field in fields){ - + var getValueDelegate = GetValueDelegateForField(field); CachedDelegatesForGettingValue[type].Add(field.Name,getValueDelegate); if (field.IsPublic){ @@ -368,24 +342,12 @@ namespace TNodeCore.Runtime.RuntimeCache{ } var methods = type.GetMethods(); foreach(var method in methods){ + //Check if the method has an [Port] attribute var portAttribute = method.GetCustomAttribute(); if(portAttribute != null){ - - //if the port is an input port. cached it as an function of setting value - - if(portAttribute is InputAttribute){ - var inputMethodAccessor = CreateMethodInputCache(method.Name, type, - method.GetParameters()[0].ParameterType); - InputMethodPorts[type].Add(method.Name,inputMethodAccessor); - - } - - if (portAttribute is OutputAttribute){ - var outputMethodAccessor = CreateMethodOutputCache(method.Name, type, - method.ReturnType); - OutputMethodPorts[type].Add(method.Name,outputMethodAccessor); - } + var propertyAccessor = CreatePortCacheForMethod(method.Name,type,method.ReturnType==typeof(void)?method.GetParameters()[0].ParameterType:method.ReturnType); + CachedPortAccessors[type].Add(method.Name,propertyAccessor); } } @@ -418,9 +380,7 @@ namespace TNodeCore.Runtime.RuntimeCache{ } - public interface IMethodAccessorOutput{ - object GetValue(object model); - } + public class ImplicitConversionHelper : IPortConverterHelper{ diff --git a/TNodeCore/Runtime/RuntimeModels.meta b/TNodeCore/Runtime/RuntimeModels.meta new file mode 100644 index 0000000..5208f66 --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e575067d9fe434fb82cae89ced2a081 +timeCreated: 1660882346 \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs b/TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs new file mode 100644 index 0000000..c10be12 --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TNode.TNodeCore.Runtime.Models; +using TNodeCore.Runtime.Models; +using UnityEngine; + +namespace TNodeCore.Runtime{ + public class ConditionalRuntimeNode:RuntimeNode{ + private readonly List>> _possibleTransition; + public ConditionalRuntimeNode(NodeData nodeData) : base(nodeData){ + + if (nodeData is ConditionalNode conditionalNode){ + var transitionPort = GetPortsOfType(); + _possibleTransition = new List>>(); + var allOutput = GetPortsOfType().Where(x => GetPortDirection(x) == Direction.Output); + var enumerable = allOutput as string[] ?? allOutput.ToArray(); + if (enumerable.Count() != transitionPort.Length){ + Debug.LogError($"Conditional node should only have output port with type of" + + $" TransitionCondition found {transitionPort.Count()} output port with" + + $" type of TransitionCondition but totally {enumerable.Count()} output port found"); + } + foreach (var port in transitionPort){ + if(GetPortDirection(port)==Direction.Input) continue; + _possibleTransition.Add(new Tuple>(port,() => (TransitionCondition)GetOutput(port)) ); + } + } + else{ + Debug.LogError("The Conditional runtime node must be applied on a Conditional node"); + } + } + + public string[] GetConditionalNextIds(){ + var ports = _possibleTransition.Where(x => x.Item2().Condition); + var portNames = ports.Select(x => x.Item1); + //Search output links to found the link contains portNames as outport's name + var outputLinks = OutputLinks.Where(x => portNames.Contains(x.outPort.portEntryName)); + return outputLinks.Select(x => x.inPort.nodeDataId).ToArray(); + } + + public string GetNextNodeId(){ + List> possibleCondition = _possibleTransition + .Select(x=>new Tuple(x.Item1,x.Item2())) + .Where(x=>x.Item2.Condition).ToList(); + possibleCondition.Sort((a, b) => { + var compareTo = b.Item2.Priority.CompareTo(a.Item2.Priority); + return compareTo; + }); + return possibleCondition.FirstOrDefault()?.Item1; + } + + } + + +} \ No newline at end of file diff --git a/TNodeCore/Runtime/ConditionalRuntimeNode.cs.meta b/TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs.meta similarity index 100% rename from TNodeCore/Runtime/ConditionalRuntimeNode.cs.meta rename to TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs.meta diff --git a/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs new file mode 100644 index 0000000..c591c6b --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using TNodeCore.Runtime.Models; + +namespace TNodeCore.Runtime.RuntimeModels{ + public interface IRuntimeNodeGraph{ + public RuntimeNode GetRuntimeNode(NodeData nodeData); + public RuntimeNode GetRuntimeNode(string id); + + public List GetRuntimeNodes(); + public Dictionary GetRuntimeNodesDictionary(); + + public NodeData GetNode(string id); + List GetRuntimeNodesOfType(Type type); + List GetRuntimeNodesOfType(); + + /// + /// Return a node if there is a node is concerned + /// + /// + public void ResetState(); + public NodeData CurrentNode(); + + public RuntimeNode MoveNext(); + public RuntimeNode CurrentRuntimeNode(); + + + } +} \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs.meta b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs.meta new file mode 100644 index 0000000..788c272 --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 40f413faa4d84765a6ac23b35a233808 +timeCreated: 1660886566 \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeNode.cs b/TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs similarity index 86% rename from TNodeCore/Runtime/RuntimeNode.cs rename to TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs index 2663762..6ff120c 100644 --- a/TNodeCore/Runtime/RuntimeNode.cs +++ b/TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs @@ -1,18 +1,20 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; using TNodeCore.Runtime.Attributes.Ports; using TNodeCore.Runtime.Models; using TNodeCore.Runtime.RuntimeCache; +using UnityEngine; namespace TNodeCore.Runtime{ public class RuntimeNode{ public NodeData NodeData { get; set; } //the link connect to node's in port - public List InputLink = new List(); + public List InputLinks = new List(); //the link connect to node's out port - public List OutputLink = new List(); + public List OutputLinks = new List(); //Cache node data type for fast access private readonly Type _type; public Type NodeType => _type; @@ -34,21 +36,21 @@ namespace TNodeCore.Runtime{ public void SetInput(string portName,object value){ var valueType = value.GetType(); - var portPath = portName.Split(':'); if (portPath.Length ==2){ portName = portPath[0]; int index = int.Parse(portPath[1]); var realPortType = GetElementTypeOfPort(portName, true); + if (realPortType != valueType){ value = RuntimeCache.RuntimeCache.Instance.GetConvertedValue(valueType,realPortType,value); } - if(realPortType.IsArray){ + if(_portAccessors[portName].Type.IsArray){ if (_portAccessors[portName].GetValue(NodeData) is Array array){ array.SetValue(value, index); } } - if (realPortType.IsGenericType){ + if (_portAccessors[portName].Type.IsGenericType){ if (_portAccessors[portName].GetValue(NodeData) is IList list) list[index] = value; } @@ -86,8 +88,7 @@ namespace TNodeCore.Runtime{ public string[] GetPortsOfType (){ var ports = new List(); foreach (var port in _portAccessors.Keys){ - - if(_portAccessors[port].Type==typeof(T)){ + if(_portAccessors[port].Type==typeof(T)||typeof(T).IsAssignableFrom(_portAccessors[port].Type)){ ports.Add(port); } } @@ -99,7 +100,7 @@ namespace TNodeCore.Runtime{ /// /// public Direction GetPortDirection(string portName){ - var attribute = NodeData.GetType().GetField(portName).GetCustomAttribute(); + var attribute = NodeData.GetType().GetMember(portName)[0].GetCustomAttribute(); if (attribute is InputAttribute){ return Direction.Input; } @@ -107,7 +108,8 @@ namespace TNodeCore.Runtime{ return Direction.Output; } - private readonly Dictionary _portAccessors; + private readonly Dictionary _portAccessors; + public Action Process; public RuntimeNode(NodeData nodeData){ @@ -115,11 +117,12 @@ namespace TNodeCore.Runtime{ //Caching the type of the node _type = nodeData.GetType(); var info = nodeData.GetType().GetProperties(); - _portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPropertyAccessors[_type]; + _portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPortAccessors[_type]; + } public List GetInputNodesId(){ List dependencies = new List(); - foreach (NodeLink link in InputLink) + foreach (NodeLink link in InputLinks) { dependencies.Add(link.outPort.nodeDataId); } diff --git a/TNodeCore/Runtime/RuntimeNode.cs.meta b/TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs.meta similarity index 100% rename from TNodeCore/Runtime/RuntimeNode.cs.meta rename to TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs.meta diff --git a/TNodeCore/Runtime/Runtimeblackboard.cs b/TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs similarity index 100% rename from TNodeCore/Runtime/Runtimeblackboard.cs rename to TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs diff --git a/TNodeCore/Runtime/Runtimeblackboard.cs.meta b/TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs.meta similarity index 100% rename from TNodeCore/Runtime/Runtimeblackboard.cs.meta rename to TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs.meta diff --git a/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs new file mode 100644 index 0000000..a5bac35 --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TNode.TNodeCore.Runtime.Models; +using TNode.TNodeCore.Runtime.Tools; +using TNodeCore.Runtime.Models; + +namespace TNodeCore.Runtime.RuntimeModels{ + public class StaticGraph:IRuntimeNodeGraph{ + private Dictionary _nodes; + private GraphTool _graphTool; + private IEnumerator BreathFirstEnumerator; + + private void ModifyLinks(NodeLink linkData){ + var outNodeId = linkData.outPort.nodeDataId; + var outNode = _nodes[outNodeId]; + outNode.OutputLinks.Add(linkData); + var inNodeId = linkData.inPort.nodeDataId; + var inNode = _nodes[inNodeId]; + inNode.InputLinks.Add(linkData); + } + public StaticGraph(List nodes,List links){ + _nodes = new Dictionary(); + + + foreach (var nodeData in nodes){ + if(_nodes.ContainsKey(nodeData.id)) continue; + + if (nodeData is ConditionalNode conditionalNode){ + ConditionalRuntimeNode conditionalRuntimeNode = new ConditionalRuntimeNode(conditionalNode); + _nodes.Add(conditionalNode.id,conditionalRuntimeNode); + } + else{ + _nodes.Add(nodeData.id,new RuntimeNode(nodeData)); + } + RuntimeNode currentNode = _nodes[nodeData.id]; + currentNode.InputLinks = new List(); + currentNode.OutputLinks = new List(); + + } + foreach (var link in links){ + ModifyLinks(link); + } + _graphTool = new GraphTool(this); + BreathFirstEnumerator = _graphTool.BreathFirstSearch(); + } + + public void ResetState(){ + BreathFirstEnumerator = _graphTool.BreathFirstSearch(); + } + + public RuntimeNode GetRuntimeNode(NodeData nodeData){ + return _nodes[nodeData.id]; + } + + public RuntimeNode GetRuntimeNode(string id){ + return _nodes[id]; + } + + public List GetRuntimeNodes(){ + return _nodes.Values.ToList(); + } + + public Dictionary GetRuntimeNodesDictionary(){ + return _nodes; + } + + public NodeData GetNode(string id){ + return _nodes[id].NodeData; + } + + public List GetRuntimeNodesOfType(Type type){ + return _nodes.Where(x=>x.Value.NodeType == type).Select(x=>x.Value).ToList(); + } + + public List GetRuntimeNodesOfType(){ + return _nodes.Where(x=>x.Value.NodeType == typeof(T)).Select(x=>x.Value).ToList(); + } + + public NodeData CurrentNode(){ + + return CurrentRuntimeNode().NodeData; + } + + public RuntimeNode MoveNext(){ + BreathFirstEnumerator.MoveNext(); + return BreathFirstEnumerator.Current; + } + + public RuntimeNode CurrentRuntimeNode(){ + if (BreathFirstEnumerator.Current == null){ + BreathFirstEnumerator.MoveNext(); + } + return BreathFirstEnumerator.Current; + } + } + + +} \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs.meta b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs.meta new file mode 100644 index 0000000..f04bcc9 --- /dev/null +++ b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5222991e09384bf68bcf7b39a1f0cb4a +timeCreated: 1660882418 \ No newline at end of file diff --git a/TNodeCore/Runtime/Tools/GraphTool.cs b/TNodeCore/Runtime/Tools/GraphTool.cs index c5f9537..ba628b0 100644 --- a/TNodeCore/Runtime/Tools/GraphTool.cs +++ b/TNodeCore/Runtime/Tools/GraphTool.cs @@ -1,10 +1,16 @@ using System; using System.Collections.Generic; +using System.Linq; using TNodeCore.Runtime; using TNodeCore.Runtime.Components; using TNodeCore.Runtime.Models; +using TNodeCore.Runtime.RuntimeModels; +using UnityEngine; namespace TNode.TNodeCore.Runtime.Tools{ + /// + /// Graph + /// public class GraphTool{ /// @@ -13,7 +19,7 @@ namespace TNode.TNodeCore.Runtime.Tools{ [NonSerialized] public readonly List TopologicalOrder = new List(); - public RuntimeGraph Parent; + public IRuntimeNodeGraph Parent; public bool TopologicalSorted = false; /// @@ -37,14 +43,73 @@ namespace TNode.TNodeCore.Runtime.Tools{ //Traverse and process all nodes in a topological order,dependency of the node is already resolved.if you want to run specific node,you can use RunNodeDependently instead public void DirectlyTraversal(){ foreach (var node in TopologicalOrder){ - var links = node.InputLink; + var links = node.InputLinks; foreach (var link in links){ HandlingLink(link); } node.NodeData.Process(); } } + //Try to enable state transition from node to node. + public IEnumerator DeepFirstSearchWithCondition(){ + Stack stack = new Stack(); + foreach (var runtimeNode in NonDependencyNode){ + stack.Push(runtimeNode); + } + while (stack.Count > 0){ + var node = stack.Pop(); + + if (node is ConditionalRuntimeNode conditionalRuntimeNode){ + var ids = conditionalRuntimeNode.GetConditionalNextIds(); + + var nextNodes = ids.Select(id=>RuntimeNodes[id]).ToList(); + + foreach (var runtimeNode in nextNodes){ + stack.Push(runtimeNode); + } + } + else{ + var nextNodes = node.OutputLinks.Select(link => RuntimeNodes[link.inPort.nodeDataId]); + foreach (var runtimeNode in nextNodes){ + stack.Push(runtimeNode); + } + node.OutputLinks.ForEach(HandlingLink); + } + node.NodeData.Process(); + yield return node; + } + } + public IEnumerator BreathFirstSearch(){ + Queue queue = new Queue(); + foreach (var runtimeNode in NonDependencyNode){ + queue.Enqueue(runtimeNode); + } + while (queue.Count > 0){ + var node = queue.Dequeue(); + if (node is ConditionalRuntimeNode conditionalRuntimeNode){ + var ids = conditionalRuntimeNode.GetConditionalNextIds(); + + var nextNodes = ids.Select(id=>RuntimeNodes[id]).ToList(); + + foreach (var runtimeNode in nextNodes){ + queue.Enqueue(runtimeNode); + } + } + else{ + foreach (var runtimeNode in node.OutputLinks.Select(link => RuntimeNodes[link.inPort.nodeDataId])){ + queue.Enqueue(runtimeNode); + } + node.OutputLinks.ForEach(HandlingLink); + } + node.NodeData.Process(); + yield return node; + } + } + + + + /// /// Cache out port data in the graph tool so that we can directly access the output. /// The two function assume there will be no change happens in scene nodes or blackboard referenced data during the running,so in a dependency traversal for some @@ -64,7 +129,7 @@ namespace TNode.TNodeCore.Runtime.Tools{ /// The node you want to resolve dependency /// search depth,no need provide a number when use outside public void RunNodeDependently(RuntimeNode runtimeNode,int dependencyLevel=0){ - var links = runtimeNode.InputLink; + var links = runtimeNode.InputLinks; foreach (var link in links){ var outputNode = RuntimeNodes[link.outPort.nodeDataId]; RunNodeDependently(outputNode,dependencyLevel+1); @@ -77,11 +142,10 @@ namespace TNode.TNodeCore.Runtime.Tools{ //if the runtime node has no output ,it will not be processed - if (runtimeNode.OutputLink.Count == 0 && dependencyLevel != 0){ + if (runtimeNode.OutputLinks.Count == 0 && dependencyLevel != 0){ return; } runtimeNode.NodeData.Process(); - Parent.StartCoroutine(runtimeNode.NodeData.AfterProcess()); } /// /// Max depth of dependency traversal,in case of some special situation. the dependency level bigger than this number will be considered as a loop. @@ -107,22 +171,37 @@ namespace TNode.TNodeCore.Runtime.Tools{ } inNode.SetInput(nodeLink.inPort.portEntryName, outValue); } + /// /// Constructor of the graph tool,it will traverse the graph and build the topological order of the graph. /// /// List of nodes you need to traversal to build graph tool /// Map stores the mapping of node data id to runtime node - - public GraphTool(List list, Dictionary graphNodes,RuntimeGraph graph){ - RuntimeNodes = graphNodes; + /// The graph you want to build graph tool for + public GraphTool(List list){ + CreateDummyRuntimeGraph(); + } + + private void CreateDummyRuntimeGraph(){ + + + + } + + public GraphTool(IRuntimeNodeGraph graph){ + RuntimeNodes = graph.GetRuntimeNodesDictionary(); + var list = graph.GetRuntimeNodes(); Parent = graph; + if (Parent == null){ + + } if (list == null) return; Queue queue = new Queue(); Dictionary inDegreeCounterForTopologicalSort = new Dictionary(); foreach (var runtimeNode in list){ var id = runtimeNode.NodeData.id; if (!inDegreeCounterForTopologicalSort.ContainsKey(id)){ - inDegreeCounterForTopologicalSort.Add(id,runtimeNode.InputLink.Count); + inDegreeCounterForTopologicalSort.Add(id,runtimeNode.InputLinks.Count); } if (inDegreeCounterForTopologicalSort[id] == 0){ queue.Enqueue(runtimeNode); @@ -134,16 +213,15 @@ namespace TNode.TNodeCore.Runtime.Tools{ while (queue.Count > 0){ var node = queue.Dequeue(); TopologicalOrder.Add(node); - foreach (var outputLink in node.OutputLink){ + foreach (var outputLink in node.OutputLinks){ inDegreeCounterForTopologicalSort[outputLink.inPort.nodeDataId]--; if (inDegreeCounterForTopologicalSort[outputLink.inPort.nodeDataId] == 0){ queue.Enqueue(RuntimeNodes[outputLink.inPort.nodeDataId]); } } } - - TopologicalSorted = TopologicalOrder.Count != list.Count; + TopologicalSorted = TopologicalOrder.Count != list.Count; inDegreeCounterForTopologicalSort.Clear(); queue.Clear(); } diff --git a/TNodeCore/Tests.meta b/TNodeCore/Tests.meta deleted file mode 100644 index f2729e1..0000000 --- a/TNodeCore/Tests.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 693e3b5ee51240dfa38db52ca14eecde -timeCreated: 1660803399 \ No newline at end of file diff --git a/TNodeCore/Tests/Editor.meta b/TNodeCore/Tests/Editor.meta deleted file mode 100644 index e7ad56f..0000000 --- a/TNodeCore/Tests/Editor.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 7390f9f15b054e6bab7abbb4afefee48 -timeCreated: 1660804507 \ No newline at end of file diff --git a/TNodeCore/Tests/Runtime.meta b/TNodeCore/Tests/Runtime.meta deleted file mode 100644 index bf8be39..0000000 --- a/TNodeCore/Tests/Runtime.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 44e6960483c34cc5bd93c350a34a1106 -timeCreated: 1660804527 \ No newline at end of file diff --git a/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs b/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs index 3b12042..6fca012 100644 --- a/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs +++ b/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs @@ -160,7 +160,7 @@ namespace TNodeGraphViewImpl.Editor.Cache{ //Outer wrapper for the singleton class public static class NodeEditorExtensions{ /// - /// by given a generic type T,return the implementation instance of the generic type + /// by given a generic type type,return the implementation instance of the generic type /// /// /// @@ -234,12 +234,12 @@ namespace TNodeGraphViewImpl.Editor.Cache{ if (t.IsGenericType){ //AKA if BlackboardDragNode is pulled - //Get BlackboardDragNode as generic type + //Get BlackboardDragNode as generic type var genericTypeDefinition = t.GetGenericTypeDefinition(); - //What you want is a BaseNodeView> to be created + //What you want is a BaseNodeView> to be created var genericViewType = typeof(BaseNodeView<>).MakeGenericType(genericTypeDefinition); //search for the specific type of genericViewType in the dictionary diff --git a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs index 0155166..d6e4413 100644 --- a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs +++ b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs @@ -189,7 +189,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ Vector2 editorPosition = Owner==null?Vector2.zero:Owner.position.position; //Remove all the previous menu items evt.menu.MenuItems().Clear(); - evt.menu.AppendAction("Create Node", dma => { + evt.menu.AppendAction("CreateProp Node", dma => { var dmaPos = dma.eventInfo.mousePosition+editorPosition; SearchWindowContext searchWindowContext = new SearchWindowContext(dmaPos,200,200); var searchWindow = ScriptableObject.CreateInstance(); @@ -199,7 +199,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ Debug.Log(targetPos); SearchWindow.Open(searchWindowContext, searchWindow); }); - evt.menu.AppendAction("Create PlacematModel",dma=> { + evt.menu.AppendAction("CreateProp PlacematModel",dma=> { //find placemat container var placematContainer = GetPlacematContainer(); var targetPos = this.viewTransform.matrix.inverse.MultiplyPoint(dma.eventInfo.localMousePosition); @@ -440,7 +440,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ } else{ - var node = _runtimeGraph.Get(runtimeNodeData.id).NodeData as SceneNode; + var node = _runtimeGraph.GetRuntimeNode(runtimeNodeData.id).NodeData as SceneNode; AddPersistentNode(node); } } diff --git a/TNodeGraphViewImpl/Editor/Search/NodeSearchWindowProvider.cs b/TNodeGraphViewImpl/Editor/Search/NodeSearchWindowProvider.cs index 640477b..48f29ad 100644 --- a/TNodeGraphViewImpl/Editor/Search/NodeSearchWindowProvider.cs +++ b/TNodeGraphViewImpl/Editor/Search/NodeSearchWindowProvider.cs @@ -30,7 +30,7 @@ namespace TNodeGraphViewImpl.Editor.Search{ var list = new List{ }; - var root = new SearchTreeGroupEntry(new GUIContent("Create"),0); + var root = new SearchTreeGroupEntry(new GUIContent("CreateProp"),0); list.Add(root); Texture2D icon = new Texture2D(2,2); foreach (var category in categories){ diff --git a/TNodeGraphViewImpl/Tests.meta b/TNodeGraphViewImpl/Tests.meta deleted file mode 100644 index f937873..0000000 --- a/TNodeGraphViewImpl/Tests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 61d88b1f3fc14972aa28c22a5afaa53e -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Tests/StaticGraphTest.cs b/Tests/StaticGraphTest.cs new file mode 100644 index 0000000..c56c808 --- /dev/null +++ b/Tests/StaticGraphTest.cs @@ -0,0 +1,189 @@ +using System.Linq; +using NUnit.Framework; +using TNode.TNodeCore.Runtime.Models; +using TNodeCore.Editor.Tools.NodeCreator; +using TNodeCore.Runtime; +using TNodeCore.Runtime.Attributes; +using TNodeCore.Runtime.Attributes.Ports; +using TNodeCore.Runtime.Models; +using TNodeCore.Runtime.RuntimeModels; +using UnityEditor.VersionControl; +using UnityEngine; + +namespace Tests{ + public class StaticGraphTest{ + internal class GraphDataForTest:GraphData{ + + } + [GraphUsage(typeof(GraphDataForTest))] + internal class TestNode : NodeData{ + [Input] public int Input{ get; set; } + [Output] public int Output{ get; set; } + } + [GraphUsage(typeof(GraphDataForTest))] + internal class TestConditionalNode : ConditionalNode{ + public bool TestCondition = false; + [Output] + public TransitionCondition Output(){ + return new TransitionCondition(){ + Condition = TestCondition + }; + } + } + + [Test] + public void TestStaticGraphCreation(){ + GraphData graphData = ScriptableObject.CreateInstance(); + var node = NodeCreator.InstantiateNodeData(); + graphData.NodeDictionary.Add(node.id,node); + Assert.AreEqual(1,graphData.NodeDictionary.Count); + + var staticGraph = new StaticGraph(graphData.NodeDictionary.Values.ToList(),graphData.NodeLinks); + Assert.NotNull(staticGraph); + Assert.AreEqual(1,staticGraph.GetRuntimeNodes().Count); + } + + [Test] + public void TestStaticGraphAccess(){ + GraphData graphData = ScriptableObject.CreateInstance(); + var node = NodeCreator.InstantiateNodeData(); + graphData.NodeDictionary.Add(node.id,node); + + + var staticGraph = new StaticGraph(graphData.NodeDictionary.Values.ToList(),graphData.NodeLinks); + + + Assert.AreEqual(staticGraph.GetRuntimeNodes().First(),staticGraph.CurrentRuntimeNode()); + } + + [Test] + public void TestStaticGraphBfs(){ + GraphData graphData = ScriptableObject.CreateInstance(); + var node1 = NodeCreator.InstantiateNodeData(); + var node2 = NodeCreator.InstantiateNodeData(); + var node3 = NodeCreator.InstantiateNodeData(); + var node4 = NodeCreator.InstantiateNodeData(); + graphData.NodeDictionary.Add(node1.id,node1); + graphData.NodeDictionary.Add(node2.id,node2); + graphData.NodeDictionary.Add(node3.id,node3); + graphData.NodeDictionary.Add(node4.id,node4); + //Link node1 to node2 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "Input", + nodeDataId = node2.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node1.id + })); + //Link node2 to node4 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "Input", + nodeDataId = node4.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node2.id + })); + //LINK NODE4 TO NODE3 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "Input", + nodeDataId = node3.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node4.id + })); + + + + + + + var staticGraph = new StaticGraph(graphData.NodeDictionary.Values.ToList(),graphData.NodeLinks); + + Assert.AreEqual(node1,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node2,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node4,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node3,staticGraph.CurrentNode()); + + } + + [Test] + public void TestStaticConditionalBfs(){ + GraphData graphData = ScriptableObject.CreateInstance(); + var node1 = NodeCreator.InstantiateNodeData(); + var node2 = NodeCreator.InstantiateNodeData(); + var node3 = NodeCreator.InstantiateNodeData(); + var node4 = NodeCreator.InstantiateNodeData(); + var node5 = NodeCreator.InstantiateNodeData(); + var node6 = NodeCreator.InstantiateNodeData(); + + + graphData.NodeDictionary.Add(node1.id,node1); + graphData.NodeDictionary.Add(node2.id,node2); + graphData.NodeDictionary.Add(node3.id,node3); + graphData.NodeDictionary.Add(node4.id,node4); + graphData.NodeDictionary.Add(node5.id,node5); + graphData.NodeDictionary.Add(node6.id,node6); + + + + //Link node1 to node2 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "Input", + nodeDataId = node2.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node1.id + })); + + //Link node2 to node3 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "In", + nodeDataId = node3.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node2.id + })); + //Link node3 to node6 + graphData.NodeLinks.Add(new NodeLink(new PortInfo{ + portEntryName = "Input", + nodeDataId = node6.id + },new PortInfo{ + portEntryName = "Output", + nodeDataId = node3.id + })); + + node3.TestCondition = true; + + var staticGraph = new StaticGraph(graphData.NodeDictionary.Values.ToList(),graphData.NodeLinks); + Assert.AreEqual(node1,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node4,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node5,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node2,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node3,staticGraph.CurrentNode()); + staticGraph.MoveNext(); + Assert.AreEqual(node6,staticGraph.CurrentNode()); + + node3.TestCondition = false; + + var staticGraph2 = new StaticGraph(graphData.NodeDictionary.Values.ToList(),graphData.NodeLinks); + Assert.AreEqual(node1,staticGraph2.CurrentNode()); + staticGraph2.MoveNext(); + Assert.AreEqual(node4,staticGraph2.CurrentNode()); + staticGraph2.MoveNext(); + Assert.AreEqual(node5,staticGraph2.CurrentNode()); + staticGraph2.MoveNext(); + Assert.AreEqual(node2,staticGraph2.CurrentNode()); + staticGraph2.MoveNext(); + Assert.AreEqual(node3,staticGraph2.CurrentNode()); + staticGraph2.MoveNext(); + Assert.AreNotEqual(node6,staticGraph2.CurrentNode()); + } + } +} \ No newline at end of file diff --git a/Tests/StaticGraphTest.cs.meta b/Tests/StaticGraphTest.cs.meta new file mode 100644 index 0000000..cc65916 --- /dev/null +++ b/Tests/StaticGraphTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 38916ca3e6bd4b27a97a8a09f1fa9fed +timeCreated: 1660895040 \ No newline at end of file