Merge pull request #36 from taoria/working-in-process

Working in process
main
taoria 3 years ago committed by GitHub
commit a323a4957a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      README.md
  2. 3
      TNode/GraphCreator.meta
  3. 3
      TNode/GraphCreator/Editor.meta
  4. 8
      TNode/GraphCreator/Editor/GraphCreator.cs
  5. 3
      TNode/GraphCreator/Editor/GraphCreator.cs.meta
  6. 3
      TNode/GraphCreator/Runtime.meta
  7. 3
      TNode/GraphCreator/Runtime/Blackboard.meta
  8. 9
      TNode/GraphCreator/Runtime/Blackboard/GraphMetaBlackboard.cs
  9. 3
      TNode/GraphCreator/Runtime/Blackboard/GraphMetaBlackboard.cs.meta
  10. 1
      TNode/GraphCreator/Runtime/GraphMetaNode.cs
  11. 3
      TNode/GraphCreator/Runtime/GraphMetaNode.cs.meta
  12. 6
      TNode/GraphCreator/Runtime/MetaGraph.cs
  13. 3
      TNode/GraphCreator/Runtime/MetaGraph.cs.meta
  14. 3
      TNode/GraphCreator/Runtime/Nodes.meta
  15. 16
      TNode/GraphCreator/Runtime/Nodes/GraphMetaNode.cs
  16. 3
      TNode/GraphCreator/Runtime/Nodes/GraphMetaNode.cs.meta
  17. 8
      TNode/Samples.meta
  18. 23
      TNode/Samples/AddNode.cs
  19. 3
      TNode/Samples/AddNode.cs.meta
  20. 8
      TNode/Samples/Editor.meta
  21. 17
      TNode/Samples/Editor/HelloEditor.asset
  22. 8
      TNode/Samples/Editor/HelloEditor.asset.meta
  23. 26
      TNode/Samples/Editor/HelloEditor.cs
  24. 14
      TNode/Samples/Editor/HelloEditor.cs.meta
  25. 17
      TNode/Samples/HelloBlackboard.cs
  26. 3
      TNode/Samples/HelloBlackboard.cs.meta
  27. 11
      TNode/Samples/HelloGraph.cs
  28. 11
      TNode/Samples/HelloGraph.cs.meta
  29. 114
      TNode/Samples/New HelloGraph.asset
  30. 8
      TNode/Samples/New HelloGraph.asset.meta
  31. 2
      TNode/TNodeCore/Editor/EditorPersistence/GraphEditorData.cs
  32. 8
      TNode/TNodeCore/Editor/GraphCreatorEditor.cs
  33. 3
      TNode/TNodeCore/Editor/GraphCreatorEditor.cs.meta
  34. 1
      TNode/TNodeCore/Editor/GraphEditor.cs
  35. 4
      TNode/TNodeCore/Editor/Resources/ScriptTemplates/ExampleGraph.cs.txt
  36. 3
      TNode/TNodeCore/Editor/Resources/ScriptTemplates/ExampleGraph.cs.txt.meta
  37. 5
      TNode/TNodeCore/Editor/Resources/ScriptTemplates/NewGraph.cs.txt
  38. 33
      TNode/TNodeCore/Editor/Resources/ScriptTemplates/NewGraphEditor.cs.txt
  39. 8
      TNode/TNodeCore/Editor/Tools/GraphEditorCreator/GraphEditorCreator.cs
  40. 5
      TNode/TNodeCore/Runtime/Attributes/ModelColor.cs
  41. 17
      TNode/TNodeCore/Runtime/Attributes/PortColor.cs
  42. 0
      TNode/TNodeCore/Runtime/Attributes/PortColor.cs.meta
  43. 2
      TNode/TNodeCore/Runtime/Attributes/ShowInNode.cs
  44. 0
      TNode/TNodeCore/Runtime/Attributes/ShowInNode.cs.meta
  45. 5
      TNode/TNodeCore/Runtime/Interfaces/IPortTypeConversion.cs
  46. 3
      TNode/TNodeCore/Runtime/Logger.meta
  47. 0
      TNode/TNodeCore/Runtime/Logger/NodeLogger.cs
  48. 0
      TNode/TNodeCore/Runtime/Logger/NodeLogger.cs.meta
  49. 5
      TNode/TNodeCore/Runtime/Models/BlackboardData.cs
  50. 96
      TNode/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs
  51. 7
      TNode/TNodeCore/Runtime/RuntimeNode.cs
  52. 28
      TNode/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs
  53. 26
      TNode/TNodeGraphViewImpl/Editor/GraphBlackboard/BlackboardDataEntry.cs
  54. 13
      TNode/TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs
  55. 2
      TNode/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
  56. 69
      TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  57. 1
      TNode/TNodeGraphViewImpl/Editor/NodeViews/DragNodeView.cs
  58. 34
      TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs
  59. 16
      TNode/TNodeGraphViewImpl/Editor/Resources/BlackboardDataEntry.uss
  60. 10
      TNode/TNodeGraphViewImpl/Editor/Search/BlackboardSearchWindowProvider.cs
  61. 8
      TNode/TNodeGtfImpl.meta

@ -3,34 +3,29 @@ Node graph creation tool based on unity experimental graphview and if possible l
the main goal of the repo is to make graph creation easier and more intuitive.
Note **it's not usable and productive on current stage** and need a better
development .
The tool separate its graph editor implementation and the graph creation logic.
# Install
currently under development
# Some Features
* Create graph script a the creator tool
* Node creation based on specified type of graph
* creator tool create graph easily
* Easy port creation via attribute
* Runtime graph
* Blackboard for runtime graph as exposed parameters
* Runtime graph execution
* An easy test mode (Runtime graph only)
* Scene object nodes hold scene objects
* Scene object nodes hold scene objects like blackboard
# Some to-dos
* Port connectivity of two types have implicit conversion
* Node placement
* Vertical node
* A universal merger handle multiple input
* Support static graph data traversal
# Install & Usage
Right now this lib is still under development.
# Some To-dos
* Function as port
* Circular dependency support for some situations such as FSM
* Edge colors customization
# Usage
Not yet documented
### Convention

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b880945b54834ff892ab441f95ebf64a
timeCreated: 1659601013

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 088ae3752f0e44ad91bf019cc64536fc
timeCreated: 1659601180

@ -0,0 +1,8 @@
using TNode.GraphCreator.Runtime;
using TNodeCore.Editor;
namespace TNode.GraphCreator.Editor{
public class GraphCreator:GraphEditor<MetaGraph>{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b727fc33cca34734bff028a952645808
timeCreated: 1659601005

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e10ab803f7cf4762b5e72874dc3a69a0
timeCreated: 1659601201

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a0683e43c164065927b50fc72d9065c
timeCreated: 1659601323

@ -0,0 +1,9 @@
using TNodeCore.Editor.EditorPersistence;
using TNodeCore.Runtime.Models;
namespace TNode.GraphCreator.Runtime.Blackboard{
public class GraphMetaBlackboard:BlackboardData{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 33c75ee828f4484abbe9009859357182
timeCreated: 1659601335

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4bc602c13c042819a553d6142faf3ba
timeCreated: 1659601223

@ -0,0 +1,6 @@
using TNodeCore.Runtime.Models;
namespace TNode.GraphCreator.Runtime{
public class MetaGraph : GraphData{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0047d8d81f646128eab9c94957129f4
timeCreated: 1659601169

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a54e22f7a85c4befbf19d09e14259869
timeCreated: 1659601272

@ -0,0 +1,16 @@
using System;
using TNodeCore.Runtime.Attributes;
namespace TNode.GraphCreator.Runtime.Nodes{
using TNodeCore.Runtime.Models;
namespace TNode.GraphCreator.Runtime{
[GraphUsage(typeof(MetaGraph))]
[Serializable]
public class GraphMetaNode:NodeData{
[ShowInNode]
public string createNodeName;
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f00e9ec34ac442da5e7b7b21042e37b
timeCreated: 1659601280

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

@ -0,0 +1,23 @@

using TNodeCore.Runtime;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Models;
using UnityEngine;
namespace Samples{
[GraphUsage(typeof(HelloGraph),"Math")]
public class AddNode:NodeData{
[Input]
public Vector3 A{ get; set; }
[Input]
public Vector2 B{ get; set; }
[Output]
public Vector3 Res{ get; set; }
public override void Process(){
Res = A + (Vector3)B;
this.Log(Res.ToString());
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9814ff421124feda824b77793cf24f2
timeCreated: 1659705932

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

@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 99ad0af56b40495cb6bd6165e652266c, type: 3}
m_Name: HelloEditor
m_EditorClassIdentifier:
graphElementsData: []
graphImplType: 0
autoUpdate: 0

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 060ae44fcac9b534f9bdca3c3e5f1484
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,26 @@
using Samples;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using TNodeCore.Editor;
public class HelloEditor : GraphEditor<HelloGraph>{
[OnOpenAsset]
public static bool OnOpenAsset(int instanceID, int line){
var graph = EditorUtility.InstanceIDToObject(instanceID) as HelloGraph;
if (graph != null) {
var wnd = GetWindow<HelloEditor>();
wnd.titleContent = new GUIContent("HelloGraph Editor");
wnd.Show();
wnd.SetupNonRuntime(graph);
return true;
}
return false;
}
[MenuItem("Window/HelloEditor")]
public static void ShowWindow(){
var res = GetWindow<HelloEditor>();
res.titleContent = new GUIContent("HelloGraph Editor");
res.Show();
}
}

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 7bffb928791aff340912bba71fc356e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- m_ViewDataDictionary: {instanceID: 0}
- mVisualTreeAsset: {fileID: 9197481963319205126, guid: b67f6dcbe2361b649ad2b7845207321b, type: 3}
- graphEditorData: {fileID: 11400000, guid: 060ae44fcac9b534f9bdca3c3e5f1484, type: 2}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using Samples;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Models;
using UnityEngine;
namespace TNode.Samples{
[GraphUsage(typeof(HelloGraph))]
public class HelloBlackboard:BlackboardData{
public string HelloString = "Hello World";
public List<Vector3> V3S;
public List<Vector2> V2S;
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edaf56bfb57443eb85ecadd142c4ce7d
timeCreated: 1659706002

@ -0,0 +1,11 @@
using System;
using TNodeCore.Runtime.Models;
using UnityEngine;
namespace Samples{
[CreateAssetMenu(fileName = "New HelloGraph", menuName = "TNode/HelloGraph")]
[Serializable]
public class HelloGraph : GraphData{
}
}

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2051a0adbd1ba974084a535dd06ab7d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,114 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2051a0adbd1ba974084a535dd06ab7d7, type: 3}
m_Name: New HelloGraph
m_EditorClassIdentifier:
nodeList:
- rid: 4804121563801583862
- rid: 4804121563801583898
- rid: 4804121563801583870
- rid: 4804121563801583866
nodeLinks:
- inPort:
portEntryName: A
nodeDataId: 6ceba867-fe0d-40c3-9d30-2d5d12803b52
outPort:
portEntryName: Value
nodeDataId: ac84573e-638d-45fa-b4e2-1a81c31fa9e7
- inPort:
portEntryName: B
nodeDataId: 6ceba867-fe0d-40c3-9d30-2d5d12803b52
outPort:
portEntryName: Value
nodeDataId: b2ab4a52-e65d-4104-8891-dc316af217d9
blackboardData:
rid: 4804121563801583854
sceneReference:
references:
version: 2
RefIds:
- rid: 4804121563801583854
type: {class: HelloBlackboard, ns: TNode.Samples, asm: Assembly-CSharp}
data:
positionInView:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
HelloString: Hello World
V3S:
- {x: 0, y: 0, z: 0}
- {x: 0, y: 0, z: 0}
- {x: 0, y: 0, z: 0}
V2S:
- {x: 0, y: 0}
- {x: 0, y: 0}
- {x: 0, y: 0}
- rid: 4804121563801583862
type: {class: BlackboardDragNodeData, ns: TNodeCore.Runtime.Models, asm: NewAssembly}
data:
positionInView:
serializedVersion: 2
x: 256
y: 264
width: 0
height: 0
id: bae506a7-58ec-4b79-9c21-747fa2b6a7ba
nodeName:
entryPoint: 0
isTest: 0
blackDragData: HelloString
isListElement: 0
- rid: 4804121563801583866
type: {class: AddNode, ns: Samples, asm: Assembly-CSharp}
data:
positionInView:
serializedVersion: 2
x: 620
y: 234
width: 0
height: 0
id: 6ceba867-fe0d-40c3-9d30-2d5d12803b52
nodeName: AddNode
entryPoint: 0
isTest: 0
- rid: 4804121563801583870
type: {class: BlackboardDragNodeData, ns: TNodeCore.Runtime.Models, asm: NewAssembly}
data:
positionInView:
serializedVersion: 2
x: 469.00006
y: 336
width: 0
height: 0
id: b2ab4a52-e65d-4104-8891-dc316af217d9
nodeName:
entryPoint: 0
isTest: 0
blackDragData: V2S.0
isListElement: 1
- rid: 4804121563801583898
type: {class: BlackboardDragNodeData, ns: TNodeCore.Runtime.Models, asm: NewAssembly}
data:
positionInView:
serializedVersion: 2
x: 469
y: 213
width: 0
height: 0
id: ac84573e-638d-45fa-b4e2-1a81c31fa9e7
nodeName:
entryPoint: 0
isTest: 0
blackDragData: V3S.0
isListElement: 1

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 490933fc590be444780d73cd9f777ed4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

@ -23,8 +23,6 @@ namespace TNodeCore.Editor.EditorPersistence{
return (IDataGraphView<T>)GraphViewImplCreator.Invoke(typeof(T));
}
case GraphImplType.GraphToolsFoundationImpl:
throw new NotImplementedException();
default:

@ -0,0 +1,8 @@
using UnityEditor;
namespace TNodeCore.Editor{
public class GraphCreatorEditor:EditorWindow{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 059e73589b764d7db8e53b75c86d634d
timeCreated: 1659600695

@ -80,6 +80,7 @@ namespace TNodeCore.Editor{
GraphView.IsRuntimeGraph = false;
}
private void BuildGraphView(){
GraphView = graphEditorData.GetGraphView<T>();
GraphView.Owner = this;

@ -0,0 +1,4 @@

public class Example$GraphClassName$Node:NodeData{
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 608d45120d7a4f5ca5091a1427d8843a
timeCreated: 1659602294

@ -1,7 +1,6 @@
using TNode.Models;
using UnityEngine;
using UnityEditor;
using UnityEngine;
using System;
using TNodeCore.Runtime.Models;
[CreateAssetMenu(fileName = "New $GraphClassName$", menuName = "TNode/$GraphClassName$")]
[Serializable]
public class $GraphClassName$ : GraphData{

@ -1,22 +1,25 @@
using TNode.Editor;
using UnityEditor;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
using System;
using TNodeCore.Editor;
public class $EditorClassName$ : GraphEditor<$GraphClassName$>{
[OnOpenAsset]
public static bool OnOpenAsset(int instanceID, int line){
var graph = EditorUtility.InstanceIDToObject(instanceID) as $GraphClassName$;
if (graph != null)
{
var wnd = GetWindow<$EditorClassName$>();
wnd.titleContent = new GUIContent("$GraphClassName$ Editor");
wnd.CreateGUI();
wnd._graphView.Data = graph;
return true;
}
return false;
var graph = EditorUtility.InstanceIDToObject(instanceID) as $GraphClassName$;
if (graph != null) {
var wnd = GetWindow<$EditorClassName$>();
wnd.titleContent = new GUIContent("$GraphClassName$ Editor");
wnd.Show();
wnd.SetupNonRuntime(graph);
return true;
}
return false;
}
[MenuItem("Window/$EditorClassName$")]
public static void ShowWindow(){
var res = GetWindow<$EditorClassName$>();
res.titleContent = new GUIContent("$GraphClassName$ Editor");
res.Show();
}
}

@ -26,7 +26,7 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{
GraphEditorCreator wnd = GetWindow<GraphEditorCreator>();
wnd.titleContent = new GUIContent("GraphEditorCreator");
//Set position to the center of the screen
wnd.position = new(Screen.width / 2, Screen.height / 2, 500, 300);
wnd.position = new(Screen.width / 2.0f, Screen.height / 2.0f, 500, 300);
//set this window non resizable
wnd.minSize = new Vector2(500, 300);
wnd.maxSize = new Vector2(500, 300);
@ -125,13 +125,13 @@ namespace TNodeCore.Editor.Tools.GraphEditorCreator{
var source = _sourceGeneratorForGraphEditor.GenerateGraphEditor(editorName, graphName);
var sourceGraph = _sourceGeneratorForGraphEditor.GenerateGraph(graphName);
var sourceGraphView = _sourceGeneratorForGraphEditor.GenerateGraphView(graphViewName, graphName);
//var sourceGraphView = _sourceGeneratorForGraphEditor.GenerateGraphView(graphViewName, graphName);
string editorPath = Path.Combine(path, editorName + ".cs");
string graphPath = Path.Combine(pathBeforeEditor, graphName + ".cs");
string graphViewPath = Path.Combine(path, graphViewName + ".cs");
//string graphViewPath = Path.Combine(path, graphViewName + ".cs");
File.WriteAllText(editorPath, source);
File.WriteAllText(graphPath, sourceGraph);
File.WriteAllText(graphViewPath, sourceGraphView);
//File.WriteAllText(graphViewPath, sourceGraphView);
//Refresh the AssetDatabase to import the new file
AssetDatabase.Refresh();

@ -1,5 +0,0 @@
namespace TNodeCore.Runtime.Attributes{
public class ModelColor{
}
}

@ -0,0 +1,17 @@
using System;
using UnityEngine;
namespace TNodeCore.Runtime.Attributes{
/// <summary>
/// this attribute only works on implemented types
/// </summary>
public class PortColorAttribute : Attribute{
public Color Color;
public PortColorAttribute(float r, float g, float b){
Color = new Color(r, g, b);
}
public PortColorAttribute(int r, int g,int b){
Color = new Color(r/255.0f, g/255.0f, b/255.0f);
}
}
}

@ -3,7 +3,7 @@
namespace TNodeCore.Runtime.Attributes{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class ShowInNodeViewAttribute:System.Attribute{
public class ShowInNode:System.Attribute{
}
}

@ -3,5 +3,10 @@
public abstract class PortTypeConversion<TFrom, TTo>{
public abstract TTo Convert(TFrom tFrom);
}
public abstract class TwoWayPortTypeConversion<TFrom, TTo> : PortTypeConversion<TFrom,TTo>{
public abstract TFrom ConvertBack(TTo tTo);
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f70fd0a222b9459093dff6c2b0679c3c
timeCreated: 1659682655

@ -7,10 +7,7 @@ namespace TNodeCore.Runtime.Models{
/// </summary>
[Serializable]
public class BlackboardData:Model,ICloneable{
public abstract class BlackboardData:Model{
}
}

@ -46,10 +46,22 @@ namespace TNodeCore.Runtime.RuntimeCache{
return _converter.Convert((T1)value);
}
}
//Store a t1 to t2 conversion but use two way converter's convert back method
internal class PortConverterHelperReverse<T1, T2> : IPortConverterHelper{
private readonly TwoWayPortTypeConversion<T2, T1> _converter;
public object Convert(object value){
return _converter.ConvertBack((T1)value);
}
public PortConverterHelperReverse(Type type){
_converter = Activator.CreateInstance(type) as TwoWayPortTypeConversion<T2, T1>;
}
}
internal interface IPortConverterHelper{
public object Convert(object value);
}
public class PropertyNotFoundException : Exception{
@ -128,16 +140,15 @@ namespace TNodeCore.Runtime.RuntimeCache{
}
private void CacheRuntimePortTypeConversion(Type type){
if (type.BaseType == null) return;
if (type.BaseType != null){
var genericType = type.BaseType.GetGenericTypeDefinition();
if (genericType != typeof(PortTypeConversion<,>)){
if (genericType != typeof(PortTypeConversion<,>)|| genericType != typeof(TwoWayPortTypeConversion<,>)){
return;
}
}
else{
return;
}
//Forward direction
var type1 = type.BaseType.GetGenericArguments()[0];
var type2 = type.BaseType.GetGenericArguments()[1];
var specificType = typeof(PortConverterHelper<,>).MakeGenericType(type1, type2);
@ -149,20 +160,74 @@ namespace TNodeCore.Runtime.RuntimeCache{
CachedPortConverters.Add(type1,new Dictionary<Type,IPortConverterHelper>());
}
CachedPortConverters[type1].Add(type2,instance);
//Reverse direction
if(type.BaseType.GetGenericTypeDefinition()==typeof(TwoWayPortTypeConversion<,>)){
var specificTypeReverse = typeof(PortConverterHelperReverse<,>).MakeGenericType(type2, type1);
var instanceReverse = Activator.CreateInstance(specificTypeReverse, type) as IPortConverterHelper;
if (instanceReverse == null){
return;
}
if (!CachedPortConverters.ContainsKey(type2)){
CachedPortConverters.Add(type2,new Dictionary<Type,IPortConverterHelper>());
}
CachedPortConverters[type2].Add(type1,instanceReverse);
}
}
private readonly Dictionary<Tuple<Type,Type>,bool> _possibleImplicitConversions = new ();
public bool HasImplicitConversion(Type baseType, Type targetType){
var tuple = new Tuple<Type, Type>(baseType, targetType);
if (_possibleImplicitConversions.ContainsKey(tuple)){
return _possibleImplicitConversions[tuple];
}
var res =baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
.Any(mi => {
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == baseType;
});
return _possibleImplicitConversions[tuple] = res;
}
private void CachingImplicitConversion(Type baseType, Type targetType){
if (HasImplicitConversion(baseType, 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)){
throw new ConversionFailedException("No converter found for type "+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.
if(HasImplicitConversion(from,to)){
CachingImplicitConversion(from,to);
}
}
if(!CachedPortConverters[from].ContainsKey(to)){
//Just like above, this function should be checked in here too
if(HasImplicitConversion(from,to)){
CachingImplicitConversion(from,to);
}
return value;
}
return CachedPortConverters[from][to].Convert(value);
}
private bool GetImplcitConvertedValue(Type from, Type to){
throw new NotImplementedException();
}
public List<Type> GetSupportedTypes(Type type){
if(!CachedPortConverters.ContainsKey(type)){
return null;
return new List<Type>();
}
return CachedPortConverters[type].Keys.ToList();
}
@ -248,6 +313,25 @@ namespace TNodeCore.Runtime.RuntimeCache{
}
public class ImplicitConversionHelper<T1,T2> : IPortConverterHelper{
public Func<T1, T2> ConvertFunc;
public ImplicitConversionHelper(){
//Caching the implicit method that converts t1 to t2
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);
}
//Create the delegate
if (method != null)
ConvertFunc = (Func<T1, T2>) Delegate.CreateDelegate(typeof(Func<T1, T2>), method);
}
public object Convert(object value){
return ConvertFunc((T1) value);
}
}
public class ConversionFailedException : Exception{
public ConversionFailedException(string noConverterFoundForType):base(noConverterFoundForType){

@ -23,16 +23,15 @@ namespace TNodeCore.Runtime{
var portType = _portAccessors[portName].Type;
if(portType!=valueType && !portType.IsAssignableFrom(valueType)){
var res =RuntimeCache.RuntimeCache.Instance.GetConvertedValue(valueType, portType, value);
_portAccessors[portName].SetValue(this.NodeData, res);
_portAccessors[portName].SetValue(NodeData, res);
}
else{
_portAccessors[portName].SetValue(this.NodeData,value);
_portAccessors[portName].SetValue(NodeData,value);
}
}
public object GetOutput(string portName){
return _portAccessors[portName].GetValue(this.NodeData);
return _portAccessors[portName].GetValue(NodeData);
}

@ -10,7 +10,9 @@ using TNodeCore.Editor.EditorPersistence;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Models;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
namespace TNode.TNodeGraphViewImpl.Editor.Cache{
/// <summary>
@ -40,7 +42,12 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{
get{ return _instance ??= new NodeEditorSingleton(); }
}
private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"};
private static readonly string[] ExcludedAssemblies = new[]{
"Microsoft", "UnityEngine","UnityEditor","mscorlib",
"System","Mono","PlasticPipe","unityplastic","ExCSS",
"Unity","PlayerBuildProgramLibrary","netstandard","log4net","Newtonsoft","Bee","nunit","PsdPlugin"
};
public static T CreateViewComponentFromBaseType<T>(){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[typeof(T)];
var instance = (T)Activator.CreateInstance(implementedType);
@ -63,10 +70,12 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{
private NodeEditorSingleton(){
//exclude unity ,system ,and microsoft types
var assemblies = AppDomain.
CurrentDomain.GetAssemblies()
.Where(x=>ExcludedAssemblies.All(y=>!x.GetName().Name.Split(".")[0].Equals(y)));
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
assemblies = assemblies.Where(x => !ExcludedAssemblies.Contains(x.FullName.Split('.',',',' ')[0])).ToArray();
foreach (var ass in assemblies){
}
foreach(var assembly in assemblies){
foreach(var type in assembly.GetTypes()){
if(type.IsClass && !type.IsAbstract){
@ -84,9 +93,14 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{
private IBaseDataGraphView GraphViewImplCreator(Type arg){
var genericType = typeof(BaseDataGraphView<>).MakeGenericType(arg);
var instance = CreateViewComponentFromBaseType(genericType) as IBaseDataGraphView;
if (instance == null){
//fallback to default graph view
instance = (IBaseDataGraphView) Activator.CreateInstance(genericType);
}
return instance;
}
private void SetGraphUsageAttribute(Type type){
foreach (var attribute in type.GetCustomAttributes(typeof(GraphUsageAttribute), true)){
var parent = type.BaseType;
if (typeof(Model).IsAssignableFrom(type.BaseType)){
@ -201,11 +215,15 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{
}
public static List<string> GetGraphCategories(Type t){
if(!NodeEditorSingleton.Instance.GraphDataUsage.ContainsKey(t)){
return new List<string>();
}
var list = NodeEditorSingleton.Instance.GraphDataUsage[t];
//Merge same category
var res = list.Select(x=>x.GetCustomAttribute<GraphUsageAttribute>().Category).Distinct().ToList();
return res;
}
//TODO Move this method to runtime place
public static BlackboardData GetAppropriateBlackboardData(Type t){
if (NodeEditorSingleton.Instance.GraphBlackboard.ContainsKey(t)){
return (BlackboardData)Activator.CreateInstance(NodeEditorSingleton.Instance.GraphBlackboard[t]);
@ -257,8 +275,10 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{
}
}
[InitializeOnLoad]
public class Launcher{
static Launcher(){
Debug.Log("NES Launched");
NodeEditorSingleton.Instance.Initialize();
}
}

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Reflection;
using TNodeCore.Runtime.Attributes;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
@ -8,37 +9,34 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{
public class BlackboardDataEntry:GraphElement{
public Type propertyType;
public string propertyPath;
private Color _convertedColor;
public BlackboardDataEntry(Type type){
propertyType = type;
if (typeof(Component).IsAssignableFrom(propertyType)){
this.AddToClassList("typeComponent");
}
if (typeof(GameObject).IsAssignableFrom(propertyType)){
}else if (typeof(GameObject).IsAssignableFrom(propertyType)){
this.AddToClassList("gameObject");
}else{
this.AddToClassList(propertyType.Name);
}
if (typeof(Vector2).IsAssignableFrom(propertyType)){
this.AddToClassList("vector");
}
if (typeof(Vector2Int).IsAssignableFrom(propertyType)){
this.AddToClassList("vector");
}
if (typeof(IList).IsAssignableFrom(propertyType)){
this.AddToClassList("list");
}
this.capabilities |= Capabilities.Selectable | Capabilities.Deletable | Capabilities.Droppable | Capabilities.Renamable;
this.AddManipulator(new SelectionDropper());
var styleSheet = Resources.Load<StyleSheet>("BlackboardDataEntry");
this.styleSheets.Add(styleSheet);
if (type.GetCustomAttribute<PortColorAttribute>() is {} portColorAttribute){
_convertedColor = portColorAttribute.Color;
}
this.RegisterCallback<MouseEnterEvent>((evt) => {
style.borderBottomColor=style.borderRightColor=style.borderLeftColor=style.borderTopColor=new Color(1,1,1,1);
});
this.RegisterCallback<MouseLeaveEvent>((evt) => {
style.borderBottomColor = style.borderRightColor =
style.borderLeftColor = style.borderTopColor = StyleKeyword.Null;
style.borderLeftColor = style.borderTopColor = _convertedColor==default?StyleKeyword.Null:_convertedColor;
});
}
}
}

@ -50,24 +50,24 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{
blackboardList.Add(foldout);
Add(blackboardList);
if(field.GetValue(data)==null) continue;
if (field.GetValue(data) is IList list){
for (var i = 0; i < list.Count; i++){
CreateBlackboardDataEntryForListItem(field, serializedObject, isRuntimeGraph, blackboardList, i);
}
}
if (field.GetValue(data).GetType().IsArray){
var array = (Array)field.GetValue(data);
if(array==null) continue;
for (var i = 0; i < array.Length; i++){
CreateBlackboardDataEntryForListItem(field, serializedObject, isRuntimeGraph, blackboardList, i);
}
}
}
}
addItemRequested += (sender) => {
addItemRequested = (sender) => {
var res = ScriptableObject.CreateInstance<BlackboardSearchWindowProvider>();
Debug.Log(res);
//Get right top corner of the blackboard
var blackboardPos = GetPosition().position+OwnerWindow.position.position;
@ -80,14 +80,14 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{
SearchWindow.Open(searchWindowContext, res);
};
}
private static void CreateBlackboardDataEntryForListItem(FieldInfo field, SerializedObject serializedObject,
bool isRuntimeGraph,
BlackboardSection blackboardSection, int index){
var property =serializedObject.FindProperty("data").FindPropertyRelative(field.Name).GetArrayElementAtIndex(index);
var property = serializedObject.FindProperty("data");
property = property.FindPropertyRelative(field.Name).GetArrayElementAtIndex(index);
BlackboardDataEntry entry = new BlackboardDataEntry(field.FieldType){
propertyPath = field.Name+"."+index,
@ -102,6 +102,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{
}
private static void CreateBlackboardDataEntry(FieldInfo field, SerializedObject serializedObject, bool isRuntimeGraph,
BlackboardSection blackboardSection){
BlackboardDataEntry entry = new BlackboardDataEntry(field.FieldType){

@ -46,7 +46,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.Inspector{
foreach (var field in _data.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public|BindingFlags.NonPublic)){
//Create corresponding property field
//check if the field has ShowInNodeView attribute
var showInNodeViewAttribute = field.GetCustomAttribute<ShowInNodeViewAttribute>() != null;
var showInNodeViewAttribute = field.GetCustomAttribute<ShowInNode>() != null;
if (!showInNodeViewAttribute)
continue;
var drawer = new PropertyField(serializedObject.FindProperty("data").FindPropertyRelative(field.Name));

@ -145,6 +145,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
if (Data != null){
visualElement.RemoveFromHierarchy();
}
CreateMenu();
};
}
@ -219,7 +220,6 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
SetDetachedFromPanel();
}
private void SetDetachedFromPanel(){
@ -241,6 +241,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
}
protected void CreateMenu(){
if (this.Q("TopMenu") != null) return;
var visualElement = new VisualElement{
name = "TopMenu"
};
@ -278,6 +279,16 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
}
});
visualElement.Add(runButton);
var blackboardButton = new Button{
name = "blackboardButton",
text = "Blackboard"
};
blackboardButton.RegisterCallback<ClickEvent>(evt => {
if(_blackboard==null)
CreateBlackboard();
});
visualElement.Add(blackboardButton);
}
public void RegisterDragEvent(){
@ -399,12 +410,14 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
}
private void BlackboardUpdate(){
private void UpdateBlackboardData(){
if (_data == null) return;
if (_data.blackboardData == null || _data.blackboardData.GetType()==(typeof(BlackboardData))){
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
Debug.Log(_data.blackboardData);
if (_data.blackboardData == null) return;
}
_blackboard.SetBlackboardData(GetBlackboardData());
}
@ -506,15 +519,45 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
});
}
public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter){
var supportedTypes = RuntimeCache.Instance.GetSupportedTypes(startPort.portType);
var compatiblePorts = ports.Where(x => startPort != x &&
(x.portType == startPort.portType ||
x.portType.IsAssignableFrom(startPort.portType)
)).ToList();
if (supportedTypes != null){
compatiblePorts.AddRange(ports.Where(x => supportedTypes.Contains(x.portType)).ToList());
(x.portType == startPort.portType ||
x.portType.IsAssignableFrom(startPort.portType)
)).ToList();
if(startPort.direction==Direction.Input){
//Search output to find ports with type that have implicit conversion or define converter that convert to type of the startPort
var outputPorts = ports.Where(x => x.direction == Direction.Output).ToList();
foreach (var outputPort in outputPorts){
//Want a port type that can convert to to the type of the startPort
if (HasImplicitConversion(outputPort.portType,startPort.portType)){
compatiblePorts.Add(outputPort);
}
if (RuntimeCache.Instance.GetSupportedTypes(outputPort.portType).Contains(startPort.portType)){
compatiblePorts.Add(outputPort);
}
}
}
else{
var inputPorts = ports.Where(x => x.direction == Direction.Input).ToList();
foreach (var inputPort in inputPorts){
//check if start port could implicitly convert to input port type
if (HasImplicitConversion(startPort.portType,inputPort.portType)){
compatiblePorts.Add(inputPort);
}
//Check if input port type is supported by output port type
if (RuntimeCache.Instance.GetSupportedTypes(startPort.portType).Contains(inputPort.portType)){
compatiblePorts.Add(inputPort);
}
}
}
return compatiblePorts;
@ -607,6 +650,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
get=>Owner.graphEditorData.autoUpdate; set=>Owner.graphEditorData.autoUpdate = value;
}
public override EventPropagation DeleteSelection(){
Undo.RegisterCompleteObjectUndo(_data,"Delete Selection");
var res = base.DeleteSelection();
@ -616,13 +660,14 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{
}
public void CreateBlackboard(){
_blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T));
_blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T)) ;
_blackboard.Setup(this,Owner);
var castedBlackboard = _blackboard as Blackboard;
Add(castedBlackboard);
Rect blackboardPos = new Rect(0,0,300,700);
castedBlackboard?.SetPosition(blackboardPos);
OnDataChanged+= (sender, e) => { BlackboardUpdate(); };
UpdateBlackboardData();
OnDataChanged+= (sender, e) => { UpdateBlackboardData(); };
}
public GraphData GetGraphData(){

@ -33,6 +33,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{
label.text = ObjectNames.NicifyVariableName(obj.BlackDragData);
//Get serialized property's icon
Texture2D icon = null;
if (serializedProperty == null) return;
if (serializedProperty.boxedValue is Object value){
icon = AssetPreview.GetMiniThumbnail(value);
}

@ -6,6 +6,7 @@ using TNode.TNodeGraphViewImpl.Editor.Ports;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Editor.Serialization;
using TNodeCore.Runtime;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Models;
using UnityEditor;
@ -141,27 +142,31 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{
Port port = new CustomPort(Orientation.Horizontal, Direction.Output,
attribute.Multiple ? Port.Capacity.Multi : Port.Capacity.Single,
BuildPortType(attribute, propertyInfo));
this.outputContainer.Add(port);
var portName = ObjectNames.NicifyVariableName(BuildPortName(attribute,propertyInfo));
port.portName = portName;
port.name = propertyInfo.Name;
BuildPort(port, attribute, propertyInfo,outputContainer);
}
}
foreach (var propertyInfo in propertyInfos){
if(propertyInfo.GetCustomAttributes(typeof(InputAttribute),true).FirstOrDefault() is InputAttribute attribute){
Port port = new CustomPort(Orientation.Horizontal, Direction.Input,attribute.Multiple?Port.Capacity.Multi:Port.Capacity.Single,BuildPortType(attribute,propertyInfo));
this.inputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName;
port.name = propertyInfo.Name;
Port port = new CustomPort
(Orientation.Horizontal,
Direction.Input,attribute.Multiple?Port.Capacity.Multi: Port.Capacity.Single,BuildPortType(attribute,propertyInfo));
BuildPort(port,attribute,propertyInfo,inputContainer);
}
}
}
private void BuildPort(Port port, PortAttribute attribute, PropertyInfo propertyInfo,VisualElement portContainer){
portContainer.Add(port);
var portName = ObjectNames.NicifyVariableName(BuildPortName(attribute, propertyInfo));
port.portName = portName;
port.name = propertyInfo.Name;
var colorAtt = propertyInfo.PropertyType.GetCustomAttribute<PortColorAttribute>();
if (colorAtt != null){
var color = colorAtt.Color;
port.portColor = color;
}
}
public void StartARenameTitleTextField(){
var textField = new TextField{
value = title,
@ -220,8 +225,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{
public override void SetPosition(Rect newPos){
var graphView = (GraphView)BaseDataGraphView;
//Cast newPos s position to global space
var globalPos = graphView.contentViewContainer.LocalToWorld(newPos.position);
_data.positionInView.position = globalPos;
_data.positionInView.position = newPos.position;
base.SetPosition(newPos);
}

@ -6,7 +6,19 @@
border-width: 1px;
border-color: rgba(201, 249, 116, 255);
}
.vector{
.Int32{
border-width: 1px;
border-color: rgba(0.788, 0.969, 0.455, 1.000);
border-color: rgba(148,130,230,255);
}
.Float{
border-width: 1px;
border-color: aquamarine;
}
.System32{
border-width: 1px;
border-color: aquamarine;
}
.String{
border-width: 1px;
border-color: crimson;
}

@ -31,17 +31,21 @@ namespace TNode.TNodeGraphViewImpl.Editor.Search{
if (list == null) throw new ArgumentNullException(nameof(list));
//search fields with List type
Texture2D icon = new Texture2D(2,2);
foreach (var field in type.GetFields()){
if (field.FieldType.IsGenericType){
var genericType = field.FieldType.GetGenericTypeDefinition();
if (genericType == typeof(List<>)){
var castedList = field.GetValue(blackboardData) as IList;
if (castedList == null){
field.SetValue(blackboardData, Activator.CreateInstance(field.FieldType));
}
list.Add(new SearchTreeEntry(new GUIContent(field.Name,icon)){
level = 1,
userData = new InternalSearchTreeUserData(){
List = field.GetValue(blackboardData) as IList,
Type = field.FieldType.GetGenericArguments()[0]
}
});
}
}
@ -64,7 +68,9 @@ namespace TNode.TNodeGraphViewImpl.Editor.Search{
if (userData is InternalSearchTreeUserData){
var list = ((InternalSearchTreeUserData) userData).List;
Debug.Log(list);
if (list == null){
}
var type = ((InternalSearchTreeUserData) userData).Type;
if (!typeof(Object).IsAssignableFrom(type)){
var newItem = Activator.CreateInstance(type);

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fcfff7d02bd7fbb41a515c4c4359aab2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Loading…
Cancel
Save