feature:static graph support,and conditional transition supported

main
taoria 3 years ago
parent 3487a24680
commit 92b3048f2b
  1. 69
      Samples/New HelloGraph.asset
  2. 10
      TNodeCore/Editor/GraphEditor.cs
  3. 2
      TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs
  4. 12
      TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs
  5. 2
      TNodeCore/Runtime/Components/ConditionalGraph.cs
  6. 90
      TNodeCore/Runtime/Components/RuntimeGraph.cs
  7. 0
      TNodeCore/Runtime/Components/SceneSerializedData.cs
  8. 0
      TNodeCore/Runtime/Components/SceneSerializedData.cs.meta
  9. 34
      TNodeCore/Runtime/ConditionalRuntimeNode.cs
  10. 0
      TNodeCore/Runtime/Extensions/DataWrapper.cs
  11. 0
      TNodeCore/Runtime/Extensions/DataWrapper.cs.meta
  12. 9
      TNodeCore/Runtime/Models/ConditionalNode.cs
  13. 1
      TNodeCore/Runtime/Models/NodeLink.cs
  14. 2
      TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs
  15. 0
      TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs.meta
  16. 128
      TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs
  17. 3
      TNodeCore/Runtime/RuntimeModels.meta
  18. 55
      TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs
  19. 0
      TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs.meta
  20. 29
      TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs
  21. 3
      TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs.meta
  22. 25
      TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs
  23. 0
      TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs.meta
  24. 0
      TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs
  25. 0
      TNodeCore/Runtime/RuntimeModels/Runtimeblackboard.cs.meta
  26. 99
      TNodeCore/Runtime/RuntimeModels/StaticGraph.cs
  27. 3
      TNodeCore/Runtime/RuntimeModels/StaticGraph.cs.meta
  28. 98
      TNodeCore/Runtime/Tools/GraphTool.cs
  29. 3
      TNodeCore/Tests.meta
  30. 3
      TNodeCore/Tests/Editor.meta
  31. 3
      TNodeCore/Tests/Runtime.meta
  32. 6
      TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs
  33. 6
      TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  34. 2
      TNodeGraphViewImpl/Editor/Search/NodeSearchWindowProvider.cs
  35. 8
      TNodeGraphViewImpl/Tests.meta
  36. 189
      Tests/StaticGraphTest.cs
  37. 3
      Tests/StaticGraphTest.cs.meta

@ -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

@ -11,17 +11,17 @@ namespace TNodeCore.Editor{
// public class SelectGraphWindow : EditorWindow{
// public EditorWindow parent;
// public Type graphType;
// public static void ShowWindow<T> (GraphEditor<T> parent) where T:GraphData{
// public static void ShowWindow<type> (GraphEditor<type> parent) where type:GraphData{
// var window = GetWindow<SelectGraphWindow>();
// 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<T>();
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();

@ -42,7 +42,7 @@ namespace TNodeCore.Editor.NodeGraphView{
public bool AutoUpdate{ get; set; }
/// <summary>
/// Create the blackboard view for the graph view.
/// CreateProp the blackboard view for the graph view.
/// </summary>
public void CreateBlackboard();
/// <summary>

@ -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<GraphEditorCreator>();
@ -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<Button>("CreateButton");
_createButton.clickable.clicked += OnCreateButtonClicked;
@ -90,7 +90,7 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{
}
private void OnCreateButtonClicked(){
//Create a new .cs file at current opened asset folder
//CreateProp a new .cs file at current opened asset folder
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (path == ""){
path = "Assets";
@ -142,7 +142,7 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{
EditorApplication.update();
}
//Create an NodeAttribute Editor Data Instance for the new graph editor
//CreateProp an NodeAttribute Editor Data Instance for the new graph editor
var graphEditorData = ScriptableObject.CreateInstance<GraphEditorData>();
graphEditorData.name = editorName;
VisualTreeAsset defaultEditorTree = Resources.Load<VisualTreeAsset>("GraphEditor");

@ -39,7 +39,7 @@ namespace TNode.TNodeCore.Runtime.Components{
var transitionNode = new List<RuntimeNode>();
foreach (var conditionalTransition in conditionalTransitions){
transitionNode.Add(Get(conditionalTransition));
transitionNode.Add(GetRuntimeNode(conditionalTransition));
}
foreach (var runtimeNode in transitionNode){
if (runtimeNode is ConditionalRuntimeNode == false){

@ -3,11 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using TNode.TNodeCore.Runtime.Tools;
using TNodeCore.Runtime.Models;
using TNodeCore.Runtime.RuntimeModels;
using UnityEditor;
using UnityEngine;
namespace TNodeCore.Runtime.Components{
public class RuntimeGraph:MonoBehaviour{
public class RuntimeGraph:MonoBehaviour,IRuntimeNodeGraph{
/// <summary>
/// Graph data reference to be used in runtime
/// </summary>
@ -41,9 +42,12 @@ namespace TNodeCore.Runtime.Components{
[NonSerialized]
private bool _build = false;
[NonSerialized] private IEnumerator<RuntimeNode> _runtimeNodeEnumerator;
/// <summary>
/// Build the graph tool and other dependencies for the runtime graph
/// </summary>
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{
/// </summary>
/// <param name="nodeData">Node data you provided</param>
/// <returns></returns>
public RuntimeNode Get(NodeData nodeData){
if(!_build)
Build();
if(RuntimeNodes.ContainsKey(nodeData.id)){
return RuntimeNodes[nodeData.id];
}
return null;
}
/// <summary>
/// Get the runtime node from an id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
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<RuntimeNode> GetRuntimeNodesOfType<T>(){
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<RuntimeNode> 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(){
@ -264,6 +265,41 @@ namespace TNodeCore.Runtime.Components{
_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<RuntimeNode> GetRuntimeNodes(){
return RuntimeNodes.Values.ToList();
}
public Dictionary<string, RuntimeNode> 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{

@ -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<Tuple<string,Func<bool>>> PossibleTransition;
public ConditionalRuntimeNode(NodeData nodeData) : base(nodeData){
if (nodeData is ConditionalNode conditionalNode){
var transitionPort = GetPortsOfType<bool>();
PossibleTransition = new List<Tuple<string,Func<bool>>>();
foreach (var port in transitionPort){
if(GetPortDirection(port)==Direction.Input) continue;
PossibleTransition.Add(new Tuple<string, Func<bool>>(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();
}
}
}

@ -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;
}
}

@ -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.

@ -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);

@ -10,67 +10,54 @@ using TNodeCore.Runtime.Models;
using UnityEngine;
namespace TNodeCore.Runtime.RuntimeCache{
public class PropAccessor<T1, T2>:IModelPropertyAccessor{
public class PortAccessor<T1, T2>:IModelPortAccessor{
public readonly Func<T1, T2> Get;
public readonly Action<T1, T2> Set;
public PropAccessor(string propName){
public PortAccessor(string name,bool property){
if (property){
Type t = typeof(T1);
MethodInfo getter = t.GetMethod("get_" + propName);
MethodInfo setter = t.GetMethod("set_" + 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<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
if(setter!=null)
Set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
}
public static PropAccessor<T1, T2> Create(string propName){
return new PropAccessor<T1, T2>(propName);
else{
Type t = typeof(T1);
MethodInfo method = t.GetMethod(name);
if (method == null){
throw new Exception("Method not found for name " + name);
}
public object GetValue(object model){
return Get((T1)model);
if (method.ReturnType != typeof(void)){
Type = method.ReturnType;
Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, method);
}
public void SetValue(object model, object value){
Set((T1)model,(T2)value);
else{
Type = method.GetParameters()[0].ParameterType;
Set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, method);
}
public Type Type{ get; set; }
}
public class MethodAccessorInput<T1, T2>:IMethodAccessorInput{
public readonly Action<T1, T2> Set;
}
public MethodAccessorInput(string methodName){
Type t = typeof(T1);
MethodInfo setter = t.GetMethod(methodName);
if (setter != null)
Set = (Action<T1, T2>) Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
public object GetValue(object model){
return Get((T1)model);
}
public void SetValue(object model, object value){
Set((T1)model,(T2)value);
}
public Type Type{ get; set; }
}
public class MethodAccessorOutput<T1, T2> : IMethodAccessorOutput{
public readonly Func<T1, T2> Get;
public MethodAccessorOutput(string methodName){
Type t = typeof(T1);
MethodInfo getter = t.GetMethod(methodName);
if(getter!=null)
Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
}
public object GetValue(object model){
return Get((T1)model);
}
}
public interface IMethodAccessorInput{
public void SetValue(object model,object o);
}
internal class PortConverterHelper<T1,T2> : IPortConverterHelper{
private readonly PortTypeConversion<T1, T2> _converter;
@ -122,12 +109,8 @@ namespace TNodeCore.Runtime.RuntimeCache{
new Dictionary<Type, Dictionary<string,GetValueDelegate>>();
public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue =
new Dictionary<Type,Dictionary<string,SetValueDelegate>>();
public readonly Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> CachedPropertyAccessors =
new Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> ();
public readonly Dictionary<Type,Dictionary<string,IMethodAccessorInput>> InputMethodPorts = new Dictionary<Type, Dictionary<string,IMethodAccessorInput>>();
public readonly Dictionary<Type, Dictionary<string, IMethodAccessorOutput>> OutputMethodPorts =
new Dictionary<Type, Dictionary<string, IMethodAccessorOutput>>();
public readonly Dictionary<Type,Dictionary<string,IModelPortAccessor>> CachedPortAccessors =
new Dictionary<Type,Dictionary<string,IModelPortAccessor>> ();
/// <summary>
/// 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,39 +303,30 @@ 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<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
CachedPropertyAccessors.Add(type,new Dictionary<string, IModelPropertyAccessor>());
InputMethodPorts.Add(type,new Dictionary<string,IMethodAccessorInput>());
OutputMethodPorts.Add(type,new Dictionary<string,IMethodAccessorOutput>());
CachedPortAccessors.Add(type,new Dictionary<string, IModelPortAccessor>());
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<PortAttribute>();
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();
@ -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<PortAttribute>();
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<T1,T2> : IPortConverterHelper{

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8e575067d9fe434fb82cae89ced2a081
timeCreated: 1660882346

@ -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<Tuple<string,Func<TransitionCondition>>> _possibleTransition;
public ConditionalRuntimeNode(NodeData nodeData) : base(nodeData){
if (nodeData is ConditionalNode conditionalNode){
var transitionPort = GetPortsOfType<TransitionCondition>();
_possibleTransition = new List<Tuple<string,Func<TransitionCondition>>>();
var allOutput = GetPortsOfType<object>().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<string, Func<TransitionCondition>>(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<Tuple<string,TransitionCondition>> possibleCondition = _possibleTransition
.Select(x=>new Tuple<string,TransitionCondition>(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;
}
}
}

@ -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<RuntimeNode> GetRuntimeNodes();
public Dictionary<string,RuntimeNode> GetRuntimeNodesDictionary();
public NodeData GetNode(string id);
List<RuntimeNode> GetRuntimeNodesOfType(Type type);
List<RuntimeNode> GetRuntimeNodesOfType<T>();
/// <summary>
/// Return a node if there is a node is concerned
/// </summary>
/// <returns></returns>
public void ResetState();
public NodeData CurrentNode();
public RuntimeNode MoveNext();
public RuntimeNode CurrentRuntimeNode();
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40f413faa4d84765a6ac23b35a233808
timeCreated: 1660886566

@ -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<NodeLink> InputLink = new List<NodeLink>();
public List<NodeLink> InputLinks = new List<NodeLink>();
//the link connect to node's out port
public List<NodeLink> OutputLink = new List<NodeLink>();
public List<NodeLink> OutputLinks = new List<NodeLink>();
//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<T> (){
var ports = new List<string>();
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{
/// <param name="portName"></param>
/// <returns></returns>
public Direction GetPortDirection(string portName){
var attribute = NodeData.GetType().GetField(portName).GetCustomAttribute<PortAttribute>();
var attribute = NodeData.GetType().GetMember(portName)[0].GetCustomAttribute<PortAttribute>();
if (attribute is InputAttribute){
return Direction.Input;
}
@ -107,7 +108,8 @@ namespace TNodeCore.Runtime{
return Direction.Output;
}
private readonly Dictionary<string, IModelPropertyAccessor> _portAccessors;
private readonly Dictionary<string, IModelPortAccessor> _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<string> GetInputNodesId(){
List<string> dependencies = new List<string>();
foreach (NodeLink link in InputLink)
foreach (NodeLink link in InputLinks)
{
dependencies.Add(link.outPort.nodeDataId);
}

@ -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<string,RuntimeNode> _nodes;
private GraphTool _graphTool;
private IEnumerator<RuntimeNode> 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<NodeData> nodes,List<NodeLink> links){
_nodes = new Dictionary<string, RuntimeNode>();
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<NodeLink>();
currentNode.OutputLinks = new List<NodeLink>();
}
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<RuntimeNode> GetRuntimeNodes(){
return _nodes.Values.ToList();
}
public Dictionary<string, RuntimeNode> GetRuntimeNodesDictionary(){
return _nodes;
}
public NodeData GetNode(string id){
return _nodes[id].NodeData;
}
public List<RuntimeNode> GetRuntimeNodesOfType(Type type){
return _nodes.Where(x=>x.Value.NodeType == type).Select(x=>x.Value).ToList();
}
public List<RuntimeNode> GetRuntimeNodesOfType<T>(){
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;
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5222991e09384bf68bcf7b39a1f0cb4a
timeCreated: 1660882418

@ -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{
/// <summary>
/// Graph
/// </summary>
public class GraphTool{
/// <summary>
@ -13,7 +19,7 @@ namespace TNode.TNodeCore.Runtime.Tools{
[NonSerialized]
public readonly List<RuntimeNode> TopologicalOrder = new List<RuntimeNode>();
public RuntimeGraph Parent;
public IRuntimeNodeGraph Parent;
public bool TopologicalSorted = false;
/// <summary>
@ -37,13 +43,72 @@ 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<RuntimeNode> DeepFirstSearchWithCondition(){
Stack<RuntimeNode> stack = new Stack<RuntimeNode>();
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<RuntimeNode> BreathFirstSearch(){
Queue<RuntimeNode> queue = new Queue<RuntimeNode>();
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;
}
}
/// <summary>
/// Cache out port data in the graph tool so that we can directly access the output.
@ -64,7 +129,7 @@ namespace TNode.TNodeCore.Runtime.Tools{
/// <param name="runtimeNode">The node you want to resolve dependency</param>
/// <param name="dependencyLevel">search depth,no need provide a number when use outside</param>
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());
}
/// <summary>
/// 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);
}
/// <summary>
/// Constructor of the graph tool,it will traverse the graph and build the topological order of the graph.
/// </summary>
/// <param name="list">List of nodes you need to traversal to build graph tool</param>
/// <param name="graphNodes">Map stores the mapping of node data id to runtime node</param>
/// <param name="graph">The graph you want to build graph tool for</param>
public GraphTool(List<NodeData> list){
CreateDummyRuntimeGraph();
}
private void CreateDummyRuntimeGraph(){
public GraphTool(List<RuntimeNode> list, Dictionary<string, RuntimeNode> graphNodes,RuntimeGraph graph){
RuntimeNodes = graphNodes;
}
public GraphTool(IRuntimeNodeGraph graph){
RuntimeNodes = graph.GetRuntimeNodesDictionary();
var list = graph.GetRuntimeNodes();
Parent = graph;
if (Parent == null){
}
if (list == null) return;
Queue<RuntimeNode> queue = new Queue<RuntimeNode>();
Dictionary<string,int> inDegreeCounterForTopologicalSort = new Dictionary<string, int>();
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,7 +213,7 @@ 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]);
@ -143,7 +222,6 @@ namespace TNode.TNodeCore.Runtime.Tools{
}
TopologicalSorted = TopologicalOrder.Count != list.Count;
inDegreeCounterForTopologicalSort.Clear();
queue.Clear();
}

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 693e3b5ee51240dfa38db52ca14eecde
timeCreated: 1660803399

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7390f9f15b054e6bab7abbb4afefee48
timeCreated: 1660804507

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 44e6960483c34cc5bd93c350a34a1106
timeCreated: 1660804527

@ -160,7 +160,7 @@ namespace TNodeGraphViewImpl.Editor.Cache{
//Outer wrapper for the singleton class
public static class NodeEditorExtensions{
/// <summary>
/// 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
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
@ -234,12 +234,12 @@ namespace TNodeGraphViewImpl.Editor.Cache{
if (t.IsGenericType){
//AKA if BlackboardDragNode<Camera> is pulled
//Get BlackboardDragNode<T> as generic type
//Get BlackboardDragNode<type> as generic type
var genericTypeDefinition = t.GetGenericTypeDefinition();
//What you want is a BaseNodeView<BlackboardDragNode<T>> to be created
//What you want is a BaseNodeView<BlackboardDragNode<type>> to be created
var genericViewType = typeof(BaseNodeView<>).MakeGenericType(genericTypeDefinition);
//search for the specific type of genericViewType in the dictionary

@ -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<NodeSearchWindowProvider>();
@ -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);
}
}

@ -30,7 +30,7 @@ namespace TNodeGraphViewImpl.Editor.Search{
var list = new List<SearchTreeEntry>{
};
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){

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 61d88b1f3fc14972aa28c22a5afaa53e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -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<GraphData>();
var node = NodeCreator.InstantiateNodeData<TestNode>();
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<GraphData>();
var node = NodeCreator.InstantiateNodeData<TestNode>();
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<GraphData>();
var node1 = NodeCreator.InstantiateNodeData<TestNode>();
var node2 = NodeCreator.InstantiateNodeData<TestNode>();
var node3 = NodeCreator.InstantiateNodeData<TestNode>();
var node4 = NodeCreator.InstantiateNodeData<TestNode>();
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<GraphData>();
var node1 = NodeCreator.InstantiateNodeData<TestNode>();
var node2 = NodeCreator.InstantiateNodeData<TestNode>();
var node3 = NodeCreator.InstantiateNodeData<TestConditionalNode>();
var node4 = NodeCreator.InstantiateNodeData<TestConditionalNode>();
var node5 = NodeCreator.InstantiateNodeData<TestNode>();
var node6 = NodeCreator.InstantiateNodeData<TestNode>();
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());
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38916ca3e6bd4b27a97a8a09f1fa9fed
timeCreated: 1660895040
Loading…
Cancel
Save