Compare commits

...

5 Commits

  1. 54
      Samples/New HelloGraph.asset
  2. 11
      Samples/Nodes/AddNode.cs
  3. 20
      Samples/Nodes/CheckSizeNode.cs
  4. 3
      TNodeCore/Runtime/Components/ConditionalGraph.cs
  5. 2
      TNodeCore/Runtime/Logger/NodeLogger.cs
  6. 13
      TNodeCore/Runtime/Models/ConditionalNode.cs
  7. 2
      TNodeCore/Runtime/RuntimeCache/IModelPortAccessor.cs
  8. 33
      TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs
  9. 13
      TNodeCore/Runtime/RuntimeModels/ConditionalRuntimeNode.cs
  10. 7
      TNodeCore/Runtime/RuntimeModels/RuntimeNode.cs
  11. 4
      TNodeCore/Runtime/RuntimeModels/StaticGraph.cs
  12. 3
      TNodeCore/Runtime/Tools/GraphTool.cs
  13. 5
      TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs
  14. 4
      TNodeGraphViewImpl/Editor/GraphWatcherView/GraphWatcherView.cs
  15. 3
      Tests/StaticGraphTest.cs

@ -18,6 +18,7 @@ MonoBehaviour:
- id: 2
- id: 3
- id: 4
- id: 5
nodeLinks:
- inPort:
portEntryName: A
@ -37,12 +38,18 @@ MonoBehaviour:
outPort:
portEntryName: C
nodeDataId: 4300534d-023d-4b56-a0cb-39e197e68845
- inPort:
portEntryName: B
nodeDataId: 926f2eea-3403-4663-88bd-7ed16ce029fa
outPort:
portEntryName: Value
nodeDataId: 7faced8f-0880-4fc3-b26b-aac148024af8
blackboardData:
id: 5
id: 6
sceneReference:
editorModels: []
graphViewModel:
id: 6
id: 7
references:
version: 1
00000000:
@ -51,7 +58,7 @@ MonoBehaviour:
positionInView:
serializedVersion: 2
x: 674
y: 261
y: 239
width: 0
height: 0
id: 6dba1aab-0db9-45a0-b3f7-e8fe9c47c168
@ -67,8 +74,8 @@ MonoBehaviour:
data:
positionInView:
serializedVersion: 2
x: 674
y: 330
x: 676
y: 281
width: 0
height: 0
id: 5fd53e10-e727-45e3-8458-04a6ec8581c4
@ -84,8 +91,8 @@ MonoBehaviour:
data:
positionInView:
serializedVersion: 2
x: 781
y: 252
x: 786
y: 228
width: 0
height: 0
id: 4300534d-023d-4b56-a0cb-39e197e68845
@ -97,8 +104,8 @@ MonoBehaviour:
data:
positionInView:
serializedVersion: 2
x: 908
y: 252
x: 928
y: 189
width: 0
height: 0
id: f236a611-cc64-4fce-88d2-40baf7f4a490
@ -110,8 +117,8 @@ MonoBehaviour:
data:
positionInView:
serializedVersion: 2
x: 1119
y: 210
x: 1168
y: 188
width: 0
height: 0
id: 926f2eea-3403-4663-88bd-7ed16ce029fa
@ -119,6 +126,23 @@ MonoBehaviour:
entryPoint: 0
isTest: 0
00000005:
type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: Taoria.TNodeCore.Runtime}
data:
positionInView:
serializedVersion: 2
x: 1056.5
y: 315
width: 0
height: 0
id: 7faced8f-0880-4fc3-b26b-aac148024af8
nodeName:
entryPoint: 0
isTest: 0
blackboardDragTypeString: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
blackDragData: Value.0
isListElement: 1
00000006:
type: {class: HelloBlackboard, ns: TNode.Samples, asm: Assembly-CSharp}
data:
positionInView:
@ -131,9 +155,9 @@ MonoBehaviour:
HelloString:
HelloGameObject: {fileID: 0}
Value:
- 11
- 23.9
- 102.1
00000006:
00000007:
type: {class: GraphViewModel, ns: TNode.TNodeCore.Editor.Models, asm: Taoria.TNodeCore.Runtime}
data:
positionInView:
@ -143,6 +167,6 @@ MonoBehaviour:
width: 0
height: 0
id:
persistScale: 1
persistOffset: {x: -212, y: 19}
persistScale: 0.57175326
persistOffset: {x: -366, y: 193}
isBlackboardOn: 1

@ -1,15 +1,18 @@
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Models;
namespace Samples.Nodes{
[GraphUsage(typeof(HelloGraph),"Math")]
public class AddNode:NodeData{
[Input] public float A{ get; set; } = default;
[Input]
public float A{ get; set; }
[Input]
public float B{ get; set; }
public float B{ get; set; }= default;
[Output] public float C => A + B;
public override void Process(){
this.Log($"{C}");
}
}
}

@ -1,7 +1,8 @@
using TNode.TNodeCore.Runtime.Models;
using TNodeCore.Runtime;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Models;
using UnityEngine;
namespace Samples.Nodes{
[GraphUsage(typeof(HelloGraph),"Math")]
@ -10,19 +11,24 @@ namespace Samples.Nodes{
public float A{ get; set; }
[Output]
public TransitionCondition Bigger(){
return new TransitionCondition(){
public TransitionCondition<float> Bigger(){
return new TransitionCondition<float>(){
Condition = A>0,
Priority = 0
Priority = 0,
DataFunc = ()=>A
};
}
[Output]
public TransitionCondition SmallerOrEqual(){
return new TransitionCondition(){
public TransitionCondition<float> SmallerOrEqual(){
return new TransitionCondition<float>(){
Condition = A<=0,
Priority = 0
Priority = 0,
DataFunc = ()=>A
};
}
public override void Process(){
this.Log($"{A}");
}
}
}

@ -17,9 +17,6 @@ namespace TNode.TNodeCore.Runtime.Components{
}
EntryNode = entry.FirstOrDefault() as ConditionalRuntimeNode;
}
public void Run(){
var res = StepForward();
while (StepForward().MoveNext()){

@ -10,7 +10,7 @@ namespace TNodeCore.Runtime{
if (!Loggers.ContainsKey(t.id)) return;
var nodeLoggerImpl = Loggers[t.id];
nodeLoggerImpl.Log(message);
Debug.Log(message);
}
}

@ -1,9 +1,8 @@
using TNodeCore.Runtime;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Models;
using Unity.Plastic.Newtonsoft.Json.Serialization;

namespace TNode.TNodeCore.Runtime.Models{
using System;
namespace TNodeCore.Runtime.Models{
public class ConditionalNode:NodeData{
}
@ -25,6 +24,10 @@ namespace TNode.TNodeCore.Runtime.Models{
public object GetValue(){
return DataFunc.Invoke();
}
public static implicit operator T(TransitionCondition<T> condition){
return condition.DataFunc.Invoke();
}
}
public interface IBaseTransition{
public bool Condition{ get; set; }

@ -6,6 +6,8 @@ namespace TNodeCore.Runtime.RuntimeCache{
object GetValue(object model);
void SetValue(object model, object value);
void Reset(object model);
public Type Type{ get; set; }

@ -13,6 +13,8 @@ namespace TNodeCore.Runtime.RuntimeCache{
public class PortAccessor<T1, T2>:IModelPortAccessor{
public readonly Func<T1, T2> Get;
public readonly Action<T1, T2> Set;
private readonly Action<T1> _resetFunc;
private readonly T2 _defaultValue;
public PortAccessor(string name,bool property){
if (property){
Type t = typeof(T1);
@ -20,10 +22,21 @@ namespace TNodeCore.Runtime.RuntimeCache{
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);
if (Set != null){
var dummy = Activator.CreateInstance<T1>();
if (Get != null)
_defaultValue = Get(dummy);
_resetFunc += (obj) => {
Set(obj, _defaultValue);
};
}
}
else{
Type t = typeof(T1);
@ -46,6 +59,12 @@ namespace TNodeCore.Runtime.RuntimeCache{
public void SetValue(object model, object value){
Set((T1)model,(T2)value);
}
public void Reset(object model){
//Get
_resetFunc?.Invoke((T1)model);
}
public Type Type{ get; set; }
}
@ -214,19 +233,21 @@ namespace TNodeCore.Runtime.RuntimeCache{
private void CachingImplicitConversion(Type baseType, Type targetType){
if (HasImplicitConversion(baseType, targetType)) return;
if (!HasImplicitConversion(baseType, targetType)) return;
if (CachedPortConverters.ContainsKey(baseType)&&CachedPortConverters[baseType].ContainsKey(targetType)) return;
//Create Implicit Conversion Helper that caches the implicit cast function
var typeConverter = Activator.CreateInstance(typeof(ImplicitConversionHelper<,>).MakeGenericType(baseType, targetType)) as IPortConverterHelper;
if (!CachedPortConverters.ContainsKey(baseType)){
CachedPortConverters.Add(baseType,new Dictionary<Type,IPortConverterHelper>());
}
CachedPortConverters[baseType].Add(targetType,typeConverter);
}
public object GetConvertedValue(Type from,Type to,object value){
if(!CachedPortConverters.ContainsKey(from)){
//Find the cached port failed ,check if there is an implicit conversion
//This inner cache method would only run once,so add a guard to prevent it run again,even though the function itself has a guard statement.
@ -384,14 +405,20 @@ namespace TNodeCore.Runtime.RuntimeCache{
var method = typeof(T2).GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(T1) }, null);
if (method == null){
//Search it in T1
method = typeof(T1).GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(T2) }, null);
Debug.Log($"{typeof(T1)}");
method = typeof(T1).GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(x => x.ReturnType==typeof(T2) && x.Name=="op_Implicit");
}
//Create the delegate
if (method != null)
ConvertFunc = (Func<T1, T2>) Delegate.CreateDelegate(typeof(Func<T1, T2>), method);
if (ConvertFunc == null){
Debug.Log($"{method==null}");
}
}
public object Convert(object value){
return ConvertFunc((T1) value);
}
}

@ -1,18 +1,17 @@
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;
private readonly List<Tuple<string,Func<IBaseTransition>>> _possibleTransition;
public ConditionalRuntimeNode(NodeData nodeData) : base(nodeData){
if (nodeData is ConditionalNode conditionalNode){
var transitionPort = GetPortsOfType<TransitionCondition>();
_possibleTransition = new List<Tuple<string,Func<TransitionCondition>>>();
var transitionPort = GetPortsOfType<IBaseTransition>();
_possibleTransition = new List<Tuple<string,Func<IBaseTransition>>>();
var allOutput = GetPortsOfType<object>().Where(x => GetPortDirection(x) == Direction.Output);
var enumerable = allOutput as string[] ?? allOutput.ToArray();
if (enumerable.Count() != transitionPort.Length){
@ -22,7 +21,7 @@ namespace TNodeCore.Runtime{
}
foreach (var port in transitionPort){
if(GetPortDirection(port)==Direction.Input) continue;
_possibleTransition.Add(new Tuple<string, Func<TransitionCondition>>(port,() => (TransitionCondition)GetOutput(port)) );
_possibleTransition.Add(new Tuple<string, Func<IBaseTransition>>(port,() => (IBaseTransition)GetOutput(port)) );
}
}
else{
@ -39,8 +38,8 @@ namespace TNodeCore.Runtime{
}
public string GetNextNodeId(){
List<Tuple<string,TransitionCondition>> possibleCondition = _possibleTransition
.Select(x=>new Tuple<string,TransitionCondition>(x.Item1,x.Item2()))
List<Tuple<string,IBaseTransition>> possibleCondition = _possibleTransition
.Select(x=>new Tuple<string,IBaseTransition>(x.Item1,x.Item2()))
.Where(x=>x.Item2.Condition).ToList();
possibleCondition.Sort((a, b) => {
var compareTo = b.Item2.Priority.CompareTo(a.Item2.Priority);

@ -121,6 +121,12 @@ namespace TNodeCore.Runtime{
_portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPortAccessors[_type];
}
public void ResetPortValue(){
foreach (var modelPortAccessor in _portAccessors){
modelPortAccessor.Value.Reset(this.NodeData);
}
}
public List<string> GetInputNodesId(){
List<string> dependencies = new List<string>();
foreach (NodeLink link in InputLinks)
@ -129,7 +135,6 @@ namespace TNodeCore.Runtime{
}
return dependencies;
}
}
public enum Direction{
Input,

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TNode.TNodeCore.Runtime.Models;
using TNode.TNodeCore.Runtime.Tools;
using TNodeCore.Runtime.Models;
using UnityEngine;
@ -21,7 +20,6 @@ namespace TNodeCore.Runtime.RuntimeModels{
var inNodeId = linkData.inPort.nodeDataId;
var inNode = _nodes[inNodeId];
Debug.Log($"{inNode},{outNode}");
inNode.InputLinks.Add(linkData);
}
public StaticGraph(GraphData graphData){
@ -29,8 +27,6 @@ namespace TNodeCore.Runtime.RuntimeModels{
var nodes = graphData.NodeDictionary.Values.ToList();
var links = graphData.NodeLinks;
_nodes = new Dictionary<string, RuntimeNode>();
foreach (var nodeData in nodes){
if(_nodes.ContainsKey(nodeData.id)) continue;

@ -297,7 +297,8 @@ namespace TNode.TNodeCore.Runtime.Tools{
//TODO looks like this string would be too long to make a cache
var cachedKey = $"{outNode.NodeData.id}-{nodeLink.inPort.portEntryName}";
var outValue = OutputCached.ContainsKey(cachedKey) ? OutputCached[cachedKey] : outNode.GetOutput(nodeLink.outPort.portEntryName);;
var outValue = OutputCached.ContainsKey(cachedKey) ? OutputCached[cachedKey] : outNode.GetOutput(nodeLink.outPort.portEntryName);
if (_isCachingOutput){
OutputCached[cachedKey] = outValue;
}

@ -88,8 +88,11 @@ namespace TNodeGraphViewImpl.Editor.GraphBlackboard{
bool isRuntimeGraph,
BlackboardSection blackboardSection, int index){
var property = serializedObject.FindProperty("data");
property = property.FindPropertyRelative(field.Name).GetArrayElementAtIndex(index);
property = property.FindPropertyRelative(field.Name)?.GetArrayElementAtIndex(index);
if (property == null){
Debug.LogError($"Can not find property {field.Name} in {serializedObject.targetObject.name}");
}
BlackboardDataEntry entry = new BlackboardDataEntry(field.FieldType){
propertyPath = field.Name+"."+index,
};

@ -28,7 +28,6 @@ namespace TNodeGraphViewImpl.Editor.GraphWatcherView{
}
var baseNodeViews = gv.nodes.ToList().Select(x=>(IBaseNodeView)x);
var node = baseNodeViews.First(x=>x.GetNodeData().id==runtimeNodeGraph.CurrentNode().id);
Debug.Log(node.GetNodeData().id);
var nodeView = (Node)node;
_highlightedNode = nodeView;
_highlightedNode.AddToClassList("highlightNode");
@ -50,6 +49,9 @@ namespace TNodeGraphViewImpl.Editor.GraphWatcherView{
_highlightedNode.style.borderRightWidth = _highlightedNode.style.borderTopWidth = 0;
}
runtimeNodeGraph.ResetState();
foreach (var runtimeNode in runtimeNodeGraph.GetRuntimeNodes()){
runtimeNode.ResetPortValue();
}
}
};

@ -1,13 +1,12 @@
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{

Loading…
Cancel
Save