feat:test support

main
taoria 3 years ago
parent 26b37f3044
commit 82a78b84da
  1. 24
      README.md
  2. 10
      TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs
  3. 8
      TNodeCore/Editor/Resources/GraphViewBackground.uss
  4. 13
      TNodeCore/Runtime/RuntimeGraph.cs
  5. 31
      TNodeCore/Runtime/RuntimeNode.cs
  6. 99
      TNodeCore/RuntimeCache/RuntimeCache.cs
  7. 35
      TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
  8. 46
      TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  9. 8
      TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs

@ -1,6 +1,24 @@
# T-Node # T-Node
Simple wrapper for unity experimental graphview Simple wrapper for unity experimental graphview and if possible latter,GTF.
the main goal of the repo is to make graph creation easier and more intuitive.
The main goal is to make graph script easy and clean Note **it's not usable and productive on current stage** and need a better
development .
and it's mainly for my own use now.
# Install
currently under development
# Features
1. Create graph script by the creator tool
2. Node creation based on specified type of graph
3. Easy port creation via attribute
4. Runtime graph
5. Blackboard for runtime graph as exposed parameters
6. Runtime graph execution
7. Test Mode (Runtime graph only)
# Usage
Note it's not fully usable on current stage and need a better development

@ -1,4 +1,5 @@
using TNodeCore.Models; using TNodeCore.Models;
using TNodeCore.Runtime;
using UnityEngine; using UnityEngine;
namespace TNodeCore.Editor.NodeGraphView{ namespace TNodeCore.Editor.NodeGraphView{
@ -6,11 +7,20 @@ namespace TNodeCore.Editor.NodeGraphView{
public void AddTNode(NodeData nodeData, Rect rect); public void AddTNode(NodeData nodeData, Rect rect);
public void RemoveTNode(NodeData nodeData); public void RemoveTNode(NodeData nodeData);
public bool TestMode{ get; set; }
public void CreateBlackboard(); public void CreateBlackboard();
public GraphData GetGraphData(); public GraphData GetGraphData();
public BlackboardData GetBlackboardData(); public BlackboardData GetBlackboardData();
public bool IsRuntimeGraph{ get; set; } public bool IsRuntimeGraph{ get; set; }
/// <summary>
/// Null if it is not a runtime graph
/// </summary>
/// <returns></returns>
public RuntimeGraph GetRuntimeGraph();
public void SetGraphData(GraphData graph); public void SetGraphData(GraphData graph);

@ -13,3 +13,11 @@ GridBackground{
font-size: 14; font-size: 14;
} }
#TopMenu{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 24px;
background-color: #171717;
}

@ -108,16 +108,11 @@ namespace TNodeCore.Runtime{
return null; return null;
} }
//DFS search for resolving dependency //DFS search for resolving dependency
public void StartDependencyTraversal(NodeData startNode,NodeData currentNode,int level=0){ public bool ResolveDependency(NodeData startNode){
if (!_build) if (_graphTool == null)
Build(); return false;
if(_graphTool==null)
return;
_graphTool.DependencyTraversal(Get(startNode)); _graphTool.DependencyTraversal(Get(startNode));
var inputNodesId = Get(currentNode).GetInputNodesId(); return true;
foreach (var s in inputNodesId){
var runtimeNode = Get(s);
}
} }
private void ModifyOrCreateInNode(NodeLink linkData){ private void ModifyOrCreateInNode(NodeLink linkData){
var inNodeId = linkData.inPort.nodeDataId; var inNodeId = linkData.inPort.nodeDataId;

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Codice.Client.Common.TreeGrouper; using Codice.Client.Common.TreeGrouper;
using TNodeCore.Attribute.Ports;
using TNodeCore.Models; using TNodeCore.Models;
using TNodeCore.RuntimeCache; using TNodeCore.RuntimeCache;
@ -11,7 +12,8 @@ namespace TNodeCore.Runtime{
public List<NodeLink> InputLink; public List<NodeLink> InputLink;
//the link connect to node's out port //the link connect to node's out port
public List<NodeLink> OutputLink; public List<NodeLink> OutputLink;
public Type type; //Cache node data type for fast access
private readonly Type _type;
public void SetInput(string portName,object value){ public void SetInput(string portName,object value){
@ -21,10 +23,35 @@ namespace TNodeCore.Runtime{
return NodeData.GetValue(portName); return NodeData.GetValue(portName);
} }
private Dictionary<string,RuntimeCache.RuntimeCache.SetPropertyValueDelegate> _inputPorts = new();
private Dictionary<string,RuntimeCache.RuntimeCache.GetPropertyValueDelegate> _outputPorts = new();
private void CachingPorts(){
var properties = _type.GetProperties();
foreach (var propertyInfo in properties){
//Check if the property is a port
var attribute = propertyInfo.GetCustomAttributes(typeof(InputAttribute),true);
if (attribute.Length > 0){
_inputPorts.Add(propertyInfo.Name,NodeData.CacheSetProperty(propertyInfo.Name));
}
attribute = propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true);
if (attribute.Length > 0){
_outputPorts.Add(propertyInfo.Name,NodeData.CacheGetProperty(propertyInfo.Name));
}
}
}
public RuntimeNode(NodeData nodeData){ public RuntimeNode(NodeData nodeData){
NodeData = nodeData; NodeData = nodeData;
//Caching the type of the node //Caching the type of the node
type = nodeData.GetType(); _type = nodeData.GetType();
var info = nodeData.GetType().GetProperties();
CachingPorts();
} }
public List<string> GetInputNodesId(){ public List<string> GetInputNodesId(){
List<string> dependencies = new List<string>(); List<string> dependencies = new List<string>();

@ -2,8 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using PlasticPipe.PlasticProtocol.Messages;
using TNodeCore.Attribute; using TNodeCore.Attribute;
using TNodeCore.Models; using TNodeCore.Models;
using UnityEngine;
namespace TNodeCore.RuntimeCache{ namespace TNodeCore.RuntimeCache{
public class RuntimeCache{ public class RuntimeCache{
@ -14,12 +16,19 @@ namespace TNodeCore.RuntimeCache{
} }
//delegate return a value from a nodedata //delegate return a value from a nodedata
public delegate object GetValueDelegate(IModel nodeData); public delegate object GetValueDelegate(IModel nodeData);
public delegate void SetValueDelegate(object nodeData,object value); public delegate void SetValueDelegate(IModel nodeData,object value);
public delegate object GetPropertyValueDelegate();
public delegate void SetPropertyValueDelegate(object value);
public readonly Dictionary<Type, Dictionary<string,GetValueDelegate>> CachedDelegatesForGettingValue = public readonly Dictionary<Type, Dictionary<string,GetValueDelegate>> CachedDelegatesForGettingValue =
new (); new ();
public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue = public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue =
new (); new ();
public readonly Dictionary<Type,Dictionary<string,GetPropertyValueDelegate>> CachedDelegatesForGettingPropertyValue =
new ();
public readonly Dictionary<Type,Dictionary<string,SetPropertyValueDelegate>> CachedDelegatesForSettingPropertyValue =
new ();
private readonly Dictionary<Type, Type> _graphBlackboardDictionary = new Dictionary<Type, Type>(); private readonly Dictionary<Type, Type> _graphBlackboardDictionary = new Dictionary<Type, Type>();
@ -84,16 +93,16 @@ namespace TNodeCore.RuntimeCache{
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>()); CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>()); CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
var properties = type.GetProperties(); // var properties = type.GetProperties();
foreach(var property in properties){ // foreach(var property in properties){
//if the property only has a setter ,skip // //if the property only has a setter ,skip
//
var getValueDelegate = GetValueDelegateForProperty(property); // var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate); // CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate);
//
var setValueDelegate = SetValueDelegateForProperty(property); // var setValueDelegate = SetValueDelegateForProperty(property);
CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate); // CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate);
} // }
//register the fields //register the fields
var fields = type.GetFields(); var fields = type.GetFields();
foreach(var field in fields){ foreach(var field in fields){
@ -111,18 +120,20 @@ namespace TNodeCore.RuntimeCache{
if(!CachedDelegatesForGettingValue.ContainsKey(type)){ if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>()); CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>()); CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
var properties = type.GetProperties(); var properties = type.GetProperties();
foreach(var property in properties){ foreach(var property in properties){
//if the property only has a setter ,skip //if the property only has a setter ,skip
if(property.SetMethod != null){ // if(property.GetSetMethod(false) != null){
var setValueDelegate = SetValueDelegateForProperty(property); // var setValueDelegate = SetValueDelegateForProperty(property);
CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate); // CachedDelegatesForSettingPropertyValue[type].Add(property.Name,setValueDelegate);
} // }
if(property.GetMethod != null){ // if(property.GetMethod != null){
var getValueDelegate = GetValueDelegateForProperty(property); // var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate); // CachedDelegatesForGettingPropertyValue[type].Add(property.Name,getValueDelegate);
} // }
@ -142,17 +153,21 @@ namespace TNodeCore.RuntimeCache{
} }
} }
private GetValueDelegate GetValueDelegateForField(FieldInfo field){ private GetValueDelegate GetValueDelegateForField(FieldInfo field){
return field.GetValue; return field.GetValue;
} }
private SetValueDelegate SetValueDelegateForField(FieldInfo field){ private SetValueDelegate SetValueDelegateForField(FieldInfo field){
return field.SetValue; return field.SetValue;
} }
private GetValueDelegate GetValueDelegateForProperty(PropertyInfo property){ private GetPropertyValueDelegate GetValueDelegateForProperty(PropertyInfo property){
var getValueDelegate = (GetValueDelegate)Delegate.CreateDelegate(typeof(GetValueDelegate), property.GetGetMethod()); var getValueDelegate = (GetPropertyValueDelegate)Delegate.CreateDelegate(typeof(GetPropertyValueDelegate), property.GetGetMethod());
return getValueDelegate; return getValueDelegate;
} }
private SetValueDelegate SetValueDelegateForProperty(PropertyInfo property){ private SetPropertyValueDelegate SetValueDelegateForProperty(PropertyInfo property){
var setValueDelegate = (SetValueDelegate)Delegate.CreateDelegate(typeof(SetValueDelegate), property.GetSetMethod()); Debug.Log(property.GetSetMethod());
var setValueDelegate = (SetPropertyValueDelegate)Delegate.CreateDelegate(typeof(SetPropertyValueDelegate), property.GetSetMethod());
return setValueDelegate; return setValueDelegate;
} }
@ -177,12 +192,46 @@ namespace TNodeCore.RuntimeCache{
var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path]; var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path];
method.Invoke(data,value); method.Invoke(data,value);
} }
public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){ public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path]; var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
return method; return method;
} }
/// <summary>
/// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
/// </summary>
/// <param name="data"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="PropertyNotFoundException"></exception>
public static RuntimeCache.GetPropertyValueDelegate CacheGetProperty(this IModel data,string path){
var type = data.GetType();
var property = type.GetProperty(path);
if (property == null) throw new PropertyNotFoundException(path);
var instance = Delegate.CreateDelegate(typeof(RuntimeCache.GetPropertyValueDelegate), data,
property.GetGetMethod());
return instance as RuntimeCache.GetPropertyValueDelegate;
}
/// <summary>
/// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
/// </summary>
/// <param name="data"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="PropertyNotFoundException"></exception>
public static RuntimeCache.SetPropertyValueDelegate CacheSetProperty(this IModel data,string path){
var type = data.GetType();
var property = type.GetProperty(path);
if (property == null) throw new PropertyNotFoundException(path);
var instance = Delegate.CreateDelegate(typeof(RuntimeCache.SetPropertyValueDelegate), data,
property.GetSetMethod());
return instance as RuntimeCache.SetPropertyValueDelegate;
}
}
public class PropertyNotFoundException : Exception{
public PropertyNotFoundException(string path):base("Property not found :"+path){
}
} }
} }

@ -1,7 +1,9 @@
using System.Reflection; using System.Reflection;
using TNodeCore.Attribute; using TNodeCore.Attribute;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Editor.Serialization; using TNodeCore.Editor.Serialization;
using TNodeCore.Models; using TNodeCore.Models;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor; using UnityEditor;
using UnityEditor.UIElements; using UnityEditor.UIElements;
using UnityEngine; using UnityEngine;
@ -36,9 +38,7 @@ namespace TNode.Editor.Inspector{
RefreshPropertyDrawer(); RefreshPropertyDrawer();
} }
private void CreateTestButton(){
}
private void RefreshPropertyDrawer(){ private void RefreshPropertyDrawer(){
//Check if the data's type is a generic type of BlackboardDragNodeData<> //Check if the data's type is a generic type of BlackboardDragNodeData<>
if (_data.GetType().IsSubclassOf(typeof(BlackboardDragNodeData))){ if (_data.GetType().IsSubclassOf(typeof(BlackboardDragNodeData))){
@ -57,16 +57,33 @@ namespace TNode.Editor.Inspector{
} }
if (_data.isTest){ var globalTest = GetFirstAncestorOfType<IBaseNodeView>()?.BaseDataGraphView?.TestMode;
if(globalTest??false){
CreateTestButton();
}
else if (_data.isTest){
//Add a test button for the node //Add a test button for the node
var testButton = new Button(()=>{ CreateTestButton();
Debug.Log("Test button clicked");
});
testButton.text = "Test";
_data.OnTest();
Add(testButton);
} }
} }
private void CreateTestButton(){
var testButton = new Button(() => {
var test = GetFirstAncestorOfType<IBaseDataGraphView>();
if (test != null){
if(!test.IsRuntimeGraph) return;
var runtimeGraph = test.GetRuntimeGraph();
if (runtimeGraph != null){
runtimeGraph.ResolveDependency(_data);
}
_data.OnTest();
}
}){
text = "Test"
};
Add(testButton);
}
} }
} }

@ -12,13 +12,13 @@ using TNodeCore.Runtime;
using TNodeGraphViewImpl.Editor.Cache; using TNodeGraphViewImpl.Editor.Cache;
using TNodeGraphViewImpl.Editor.GraphBlackboard; using TNodeGraphViewImpl.Editor.GraphBlackboard;
using TNodeGraphViewImpl.Editor.NodeViews; using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor; using UnityEditor;
using UnityEditor.Experimental.GraphView; using UnityEditor.Experimental.GraphView;
using UnityEditor.VersionControl;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using Edge = UnityEditor.Experimental.GraphView.Edge; using Edge = UnityEditor.Experimental.GraphView.Edge;
namespace TNodeGraphViewImpl.Editor.NodeGraphView{ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
@ -65,7 +65,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale); SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
RegisterDragEvent(); RegisterDragEvent();
OnInit(); OnInit();
CheckAfterInit();
} }
/// <summary> /// <summary>
/// Probably reusable in later GTFs version /// Probably reusable in later GTFs version
@ -144,7 +144,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
} }
}; };
} }
private void CheckAfterInit(){ private void CheckDataAfterInit(){
if(Data == null){ if(Data == null){
WaitingForAGraph(); WaitingForAGraph();
} }
@ -173,7 +173,39 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
private void OnInit(){ private void OnInit(){
ConstructDefaultBehaviour(); ConstructDefaultBehaviour();
OnGraphViewCreate(); OnGraphViewCreate();
CheckDataAfterInit();
}
protected void CreateMenu(){
var visualElement = new VisualElement{
name = "TopMenu"
};
visualElement.style.position = Position.Absolute;
visualElement.style.top = 0;
visualElement.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1));
Add(visualElement);
visualElement.style.flexDirection = FlexDirection.Row;
//Add a toggle button to toggle test mode
var testModeToggle = new Toggle{
name = "TestModeToggle",
label = "Test Mode",
value = false
};
testModeToggle.RegisterValueChangedCallback(evt => {
if (evt.newValue){
TestMode = true;
}
else{
TestMode = false;
} }
});
visualElement.Add(testModeToggle);
}
public void RegisterDragEvent(){ public void RegisterDragEvent(){
RegisterCallback<DragUpdatedEvent>(OnDragUpdated); RegisterCallback<DragUpdatedEvent>(OnDragUpdated);
RegisterCallback<DragPerformEvent>(OnDragPerform); RegisterCallback<DragPerformEvent>(OnDragPerform);
@ -437,6 +469,8 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
Owner.graphEditorData.graphElementsData.RemoveAll(x => x.guid == nodeData.id); Owner.graphEditorData.graphElementsData.RemoveAll(x => x.guid == nodeData.id);
} }
public bool TestMode{ get; set; }
public void CreateBlackboard(){ public void CreateBlackboard(){
_blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T)); _blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T));
_blackboard.Setup(this,Owner); _blackboard.Setup(this,Owner);
@ -463,6 +497,10 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
public bool IsRuntimeGraph{ get; set; } public bool IsRuntimeGraph{ get; set; }
public RuntimeGraph GetRuntimeGraph(){
return _runtimeGraph;
}
public void SetGraphData(GraphData graph){ public void SetGraphData(GraphData graph){
Data = graph as T; Data = graph as T;
} }

@ -4,6 +4,7 @@ using System.Reflection;
using TNode.Editor.Inspector; using TNode.Editor.Inspector;
using TNodeCore; using TNodeCore;
using TNodeCore.Attribute.Ports; using TNodeCore.Attribute.Ports;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Editor.Serialization; using TNodeCore.Editor.Serialization;
using TNodeCore.Models; using TNodeCore.Models;
using UnityEditor.Experimental.GraphView; using UnityEditor.Experimental.GraphView;
@ -16,6 +17,12 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
protected T _data; protected T _data;
private readonly NodeInspectorInNode _nodeInspectorInNode; private readonly NodeInspectorInNode _nodeInspectorInNode;
public IBaseDataGraphView BaseDataGraphView{
get{
var visualElement = this.GetFirstAncestorOfType<IBaseDataGraphView>() as IBaseDataGraphView;
return visualElement;
}
}
public T Data{ public T Data{
get => _data; get => _data;
set{ set{
@ -180,6 +187,7 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
public NodeData GetNodeData(); public NodeData GetNodeData();
public void OnDataModified(); public void OnDataModified();
IBaseDataGraphView BaseDataGraphView{ get; }
} }
public interface INodeView<T>:IBaseNodeView where T:NodeData,new(){ public interface INodeView<T>:IBaseNodeView where T:NodeData,new(){

Loading…
Cancel
Save