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. 33
      TNodeCore/Runtime/RuntimeNode.cs
  6. 101
      TNodeCore/RuntimeCache/RuntimeCache.cs
  7. 39
      TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
  8. 46
      TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  9. 10
      TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs

@ -1,6 +1,24 @@
# 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.Runtime;
using UnityEngine;
namespace TNodeCore.Editor.NodeGraphView{
@ -6,11 +7,20 @@ namespace TNodeCore.Editor.NodeGraphView{
public void AddTNode(NodeData nodeData, Rect rect);
public void RemoveTNode(NodeData nodeData);
public bool TestMode{ get; set; }
public void CreateBlackboard();
public GraphData GetGraphData();
public BlackboardData GetBlackboardData();
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);

@ -12,4 +12,12 @@ GridBackground{
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;
}
//DFS search for resolving dependency
public void StartDependencyTraversal(NodeData startNode,NodeData currentNode,int level=0){
if (!_build)
Build();
if(_graphTool==null)
return;
public bool ResolveDependency(NodeData startNode){
if (_graphTool == null)
return false;
_graphTool.DependencyTraversal(Get(startNode));
var inputNodesId = Get(currentNode).GetInputNodesId();
foreach (var s in inputNodesId){
var runtimeNode = Get(s);
}
return true;
}
private void ModifyOrCreateInNode(NodeLink linkData){
var inNodeId = linkData.inPort.nodeDataId;

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Codice.Client.Common.TreeGrouper;
using TNodeCore.Attribute.Ports;
using TNodeCore.Models;
using TNodeCore.RuntimeCache;
@ -11,7 +12,8 @@ namespace TNodeCore.Runtime{
public List<NodeLink> InputLink;
//the link connect to node's out port
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){
@ -20,11 +22,36 @@ namespace TNodeCore.Runtime{
public object GetOutput(string 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){
NodeData = nodeData;
//Caching the type of the node
type = nodeData.GetType();
_type = nodeData.GetType();
var info = nodeData.GetType().GetProperties();
CachingPorts();
}
public List<string> GetInputNodesId(){
List<string> dependencies = new List<string>();

@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PlasticPipe.PlasticProtocol.Messages;
using TNodeCore.Attribute;
using TNodeCore.Models;
using UnityEngine;
namespace TNodeCore.RuntimeCache{
public class RuntimeCache{
@ -14,12 +16,19 @@ namespace TNodeCore.RuntimeCache{
}
//delegate return a value from a 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 =
new ();
public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue =
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>();
@ -84,16 +93,16 @@ namespace TNodeCore.RuntimeCache{
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
var properties = type.GetProperties();
foreach(var property in properties){
//if the property only has a setter ,skip
var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate);
var setValueDelegate = SetValueDelegateForProperty(property);
CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate);
}
// var properties = type.GetProperties();
// foreach(var property in properties){
// //if the property only has a setter ,skip
//
// var getValueDelegate = GetValueDelegateForProperty(property);
// CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate);
//
// var setValueDelegate = SetValueDelegateForProperty(property);
// CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate);
// }
//register the fields
var fields = type.GetFields();
foreach(var field in fields){
@ -111,19 +120,21 @@ namespace TNodeCore.RuntimeCache{
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
var properties = type.GetProperties();
foreach(var property in properties){
//if the property only has a setter ,skip
if(property.SetMethod != null){
var setValueDelegate = SetValueDelegateForProperty(property);
CachedDelegatesForSettingValue[type].Add(property.Name,setValueDelegate);
}
if(property.GetMethod != null){
var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate);
}
// if(property.GetSetMethod(false) != null){
// var setValueDelegate = SetValueDelegateForProperty(property);
// CachedDelegatesForSettingPropertyValue[type].Add(property.Name,setValueDelegate);
// }
// if(property.GetMethod != null){
// var getValueDelegate = GetValueDelegateForProperty(property);
// CachedDelegatesForGettingPropertyValue[type].Add(property.Name,getValueDelegate);
// }
}
@ -142,17 +153,21 @@ namespace TNodeCore.RuntimeCache{
}
}
private GetValueDelegate GetValueDelegateForField(FieldInfo field){
return field.GetValue;
}
private SetValueDelegate SetValueDelegateForField(FieldInfo field){
return field.SetValue;
}
private GetValueDelegate GetValueDelegateForProperty(PropertyInfo property){
var getValueDelegate = (GetValueDelegate)Delegate.CreateDelegate(typeof(GetValueDelegate), property.GetGetMethod());
private GetPropertyValueDelegate GetValueDelegateForProperty(PropertyInfo property){
var getValueDelegate = (GetPropertyValueDelegate)Delegate.CreateDelegate(typeof(GetPropertyValueDelegate), property.GetGetMethod());
return getValueDelegate;
}
private SetValueDelegate SetValueDelegateForProperty(PropertyInfo property){
var setValueDelegate = (SetValueDelegate)Delegate.CreateDelegate(typeof(SetValueDelegate), property.GetSetMethod());
private SetPropertyValueDelegate SetValueDelegateForProperty(PropertyInfo property){
Debug.Log(property.GetSetMethod());
var setValueDelegate = (SetPropertyValueDelegate)Delegate.CreateDelegate(typeof(SetPropertyValueDelegate), property.GetSetMethod());
return setValueDelegate;
}
@ -177,12 +192,46 @@ namespace TNodeCore.RuntimeCache{
var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path];
method.Invoke(data,value);
}
public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
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 TNodeCore.Attribute;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Editor.Serialization;
using TNodeCore.Models;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
@ -36,9 +38,7 @@ namespace TNode.Editor.Inspector{
RefreshPropertyDrawer();
}
private void CreateTestButton(){
}
private void RefreshPropertyDrawer(){
//Check if the data's type is a generic type of 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
var testButton = new Button(()=>{
Debug.Log("Test button clicked");
});
testButton.text = "Test";
_data.OnTest();
Add(testButton);
CreateTestButton();
}
}
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.GraphBlackboard;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.VersionControl;
using UnityEngine;
using UnityEngine.UIElements;
using Edge = UnityEditor.Experimental.GraphView.Edge;
namespace TNodeGraphViewImpl.Editor.NodeGraphView{
@ -65,7 +65,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
RegisterDragEvent();
OnInit();
CheckAfterInit();
}
/// <summary>
/// Probably reusable in later GTFs version
@ -144,7 +144,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
}
};
}
private void CheckAfterInit(){
private void CheckDataAfterInit(){
if(Data == null){
WaitingForAGraph();
}
@ -173,7 +173,39 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
private void OnInit(){
ConstructDefaultBehaviour();
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(){
RegisterCallback<DragUpdatedEvent>(OnDragUpdated);
RegisterCallback<DragPerformEvent>(OnDragPerform);
@ -437,6 +469,8 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
Owner.graphEditorData.graphElementsData.RemoveAll(x => x.guid == nodeData.id);
}
public bool TestMode{ get; set; }
public void CreateBlackboard(){
_blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T));
_blackboard.Setup(this,Owner);
@ -463,6 +497,10 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
public bool IsRuntimeGraph{ get; set; }
public RuntimeGraph GetRuntimeGraph(){
return _runtimeGraph;
}
public void SetGraphData(GraphData graph){
Data = graph as T;
}

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

Loading…
Cancel
Save