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

Working in process
main
taoria 3 years ago committed by GitHub
commit 47c1d09e68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      TNode/Attribute/Ports/BatchInputAttribute.cs
  2. 3
      TNode/Attribute/Ports/BatchInputAttribute.cs.meta
  3. 13
      TNode/Attribute/Ports/BatchOutputAttribute.cs
  4. 3
      TNode/Attribute/Ports/BatchOutputAttribute.cs.meta
  5. 6
      TNode/Attribute/Ports/InputAttribute.cs
  6. 5
      TNode/Attribute/Ports/OutputAttribute.cs
  7. 32
      TNode/Attribute/Ports/PortAttribute.cs
  8. 1
      TNode/Attribute/ShowInNodeViewAttribute.cs
  9. 175
      TNode/Editor/BaseViews/DataGraphView.cs
  10. 111
      TNode/Editor/BaseViews/NodeView.cs
  11. 95
      TNode/Editor/Cache/NodeEditorExtensions.cs
  12. 12
      TNode/Editor/GraphBlackboard/BlackboardField.cs
  13. 3
      TNode/Editor/GraphBlackboard/BlackboardField.cs.meta
  14. 3
      TNode/Editor/GraphBlackboard/BlackboardProperty.meta
  15. 14
      TNode/Editor/GraphBlackboard/BlackboardProperty/BlackboardProperty.cs
  16. 3
      TNode/Editor/GraphBlackboard/BlackboardProperty/BlackboardProperty.cs.meta
  17. 2
      TNode/Editor/GraphEditor.cs
  18. 20
      TNode/Editor/Inspector/InspectorImplementation/EnumFieldItem.cs
  19. 3
      TNode/Editor/Inspector/InspectorImplementation/EnumFieldItem.cs.meta
  20. 20
      TNode/Editor/Inspector/InspectorItem.cs
  21. 25
      TNode/Editor/Inspector/InspectorItemFactory.cs
  22. 23
      TNode/Editor/Inspector/NodeInspectorInNode.cs
  23. 13
      TNode/Editor/Inspector/TinyInspectorItem.cs
  24. 3
      TNode/Editor/Inspector/TinyInspectorItem.cs.meta
  25. 3
      TNode/Editor/Manipulators.meta
  26. 3
      TNode/Editor/NodeViews.meta
  27. 0
      TNode/Editor/NodeViews/DefaultNodeView.cs
  28. 0
      TNode/Editor/NodeViews/DefaultNodeView.cs.meta
  29. 15
      TNode/Editor/NodeViews/DragNodeView.cs
  30. 3
      TNode/Editor/NodeViews/DragNodeView.cs.meta
  31. 179
      TNode/Editor/NodeViews/NodeView.cs
  32. 0
      TNode/Editor/NodeViews/NodeView.cs.meta
  33. 17
      TNode/Editor/Search/BlackboardSearchWindowProvider.cs
  34. 4
      TNode/Editor/Search/NodeSearchWindowProvider.cs
  35. 2
      TNode/Editor/Tools/NodeCreator/NodeCreator.cs
  36. 0
      TNode/Editor/Tools/NodeCreator/NodeCreator.cs.meta
  37. 4
      TNode/JsonSerialize/JsonSerializeTool.cs
  38. 19
      TNode/Models/BlackDragNodeData.cs
  39. 4
      TNode/Models/BlackboardData.cs
  40. 21
      TNode/Models/BlackboardDragNodeData.cs
  41. 0
      TNode/Models/BlackboardDragNodeData.cs.meta
  42. 1
      TNode/Models/GraphData.cs
  43. 10
      TNode/Runtime/Runtimeblackboard.cs
  44. 3
      TNode/Runtime/Runtimeblackboard.cs.meta
  45. 64
      TNode/RuntimeCache/RuntimeCache.cs

@ -0,0 +1,5 @@
namespace TNode.Attribute.Ports{
public class BatchInputAttribute{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8745aaf35940443693af378c8214060e
timeCreated: 1657190813

@ -0,0 +1,13 @@
using System;
using UnityEditor.Experimental.GraphView;
namespace TNode.Attribute.Ports{
/// <summary>
/// Batch out port attribute could specify a batch out port,allowing large scale calculation.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class BatchOutputAttribute:PortAttribute{
public BatchOutputAttribute(string name="") : base(name){
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a6ca9cc801d4db3b73ee59cf0aef38e
timeCreated: 1657190648

@ -7,11 +7,7 @@ namespace TNode.Attribute{
[MeansImplicitUse]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class InputAttribute : PortAttribute{
public InputAttribute(string portName, Type nodeLinkType, Port.Capacity capacity, string portAccepted = "*") : base(portName, nodeLinkType, capacity, portAccepted){
}
public InputAttribute(Type nodeLinkType, Port.Capacity capacity, string portAccepted="*") : base(nodeLinkType, capacity, portAccepted){
}
public InputAttribute(string portName="*",string portAccepted = "*") :base(portName, typeof(NodeLink),Port.Capacity.Multi,portAccepted){
public InputAttribute(string name="", PortNameHandling nameHandling = PortNameHandling.Auto) : base(name, nameHandling){
}
}
}

@ -1,5 +1,6 @@
namespace TNode.Attribute.Ports{
public class OutputAttribute:System.Attribute{
public class OutputAttribute:PortAttribute{
public OutputAttribute(string name="", PortNameHandling nameHandling = PortNameHandling.Auto) : base(name, nameHandling){
}
}
}

@ -3,26 +3,24 @@ using JetBrains.Annotations;
using UnityEditor.Experimental.GraphView;
namespace TNode.Attribute{
public enum PortNameHandling{
Auto,
MemberName,
Manual,
Format,
MemberType
}
[MeansImplicitUse]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class PortAttribute:System.Attribute{
public string PortName;
public string PortAccepted;
public Type NodeLinkType;
public Port.Capacity Capacity;
public PortAttribute(string portName, Type nodeLinkType, Port.Capacity capacity,string portAccepted="*"){
PortName = portName;
PortAccepted = portAccepted;
NodeLinkType = nodeLinkType;
Capacity = capacity;
}
//Auto generate port name via variable use this attribute
public PortAttribute(Type nodeLinkType, Port.Capacity capacity, string portAccepted = "*"){
PortAccepted = portAccepted;
NodeLinkType = nodeLinkType;
Capacity = capacity;
public readonly string Name;
public readonly PortNameHandling NameHandling;
public PortAttribute(string name,PortNameHandling nameHandling=PortNameHandling.Auto){
this.Name = name;
this.NameHandling = nameHandling;
}
}
}

@ -2,6 +2,7 @@
namespace TNode.Attribute{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class ShowInNodeViewAttribute:System.Attribute{
}

@ -4,8 +4,10 @@ using System.Linq;
using System.Reflection;
using TNode.BaseViews;
using TNode.Cache;
using TNode.Editor.GraphBlackboard;
using TNode.Editor.Inspector;
using TNode.Editor.Model;
using TNode.Editor.Tools.NodeCreator;
using TNode.Models;
using Unity.VisualScripting;
using UnityEditor;
@ -109,6 +111,11 @@ namespace TNode.Editor.BaseViews{
}
*/
public abstract class DataGraphView<T>:GraphView,IDataGraphView where T:GraphData{
#region variables and properties
private T _data;
private bool _isInspectorOn;
@ -116,7 +123,9 @@ namespace TNode.Editor.BaseViews{
private NodeInspector _nodeInspector;
public GraphEditor<T> Owner;
private Dictionary<string,Node> _nodeDict = new();
private Blackboard _blackboard;
public T Data{
get{ return _data; }
set{
@ -128,11 +137,17 @@ namespace TNode.Editor.BaseViews{
}
}
public event DataChangedEventHandler OnDataChanged;
#endregion
#region event declarations
public delegate void DataChangedEventHandler(object sender, DataChangedEventArgs<T> e);
#endregion
//A Constructor for the DataGraphView ,never to override it
#region construct default behaviour
public DataGraphView(){
styleSheets.Add(Resources.Load<StyleSheet>("GraphViewBackground"));
var grid = new GridBackground();
Insert(0,grid);
@ -141,6 +156,7 @@ namespace TNode.Editor.BaseViews{
this.AddManipulator(new SelectionDragger());
this.AddManipulator(new RectangleSelector());
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
RegisterDragEvent();
OnInit();
}
private void ConstructDefaultBehaviour(){
@ -149,10 +165,7 @@ namespace TNode.Editor.BaseViews{
}
public void ConstructViewContextualMenu(){
//Rebuild the contextual menu
this.RegisterCallback<ContextualMenuPopulateEvent>(evt => {
RegisterCallback<ContextualMenuPopulateEvent>(evt => {
Vector2 editorPosition = Owner==null?Vector2.zero:Owner.position.position;
//Remove all the previous menu items
evt.menu.MenuItems().Clear();
@ -163,15 +176,57 @@ namespace TNode.Editor.BaseViews{
searchWindow.Setup(typeof(T),this,Owner);
SearchWindow.Open(searchWindowContext, searchWindow);
});
});
}
private void OnInit(){
ConstructDefaultBehaviour();
OnGraphViewCreate();
}
public void RegisterDragEvent(){
RegisterCallback<DragUpdatedEvent>(OnDragUpdated);
RegisterCallback<DragPerformEvent>(OnDragPerform);
}
#endregion
#region event callbakc
private void OnDragPerform(DragPerformEvent evt){
if (DragAndDrop.GetGenericData("DragSelection") is List<ISelectable>{Count: > 0} data){
var blackboardFields = data.OfType<BlackboardPropertyField >();
foreach (var selectable in blackboardFields){
if(selectable is { } field) {
//Make a constructor of BlackboardDragNodeData<field.PropertyType > by reflection
var specifiedType =
typeof(BlackboardDragNodeData<>).MakeGenericType(field.BlackboardProperty.PropertyType);
//Create a new instance of specified type
var dragNodeData = NodeCreator.InstantiateNodeData(specifiedType);
this.AddTNode(dragNodeData,new Rect(evt.mousePosition,new Vector2(200,200)));
}
}
}
}
private void OnDragUpdated(DragUpdatedEvent evt){
Debug.Log(evt);
//check if the drag data is BlackboardField
if (DragAndDrop.GetGenericData("DragSelection") is List<ISelectable>{Count: > 0} data){
DragAndDrop.visualMode = DragAndDropVisualMode.Move;
}
}
#endregion
public void ResetGraphView(){
//Clear all nodes
foreach (var node in nodes){
@ -206,8 +261,7 @@ namespace TNode.Editor.BaseViews{
input = inputNodeView.inputContainer.Q<Port>(edge.inPort.portName),
output = outputNodeView.outputContainer.Q<Port>(edge.outPort.portName)
};
Debug.Log(edge.inPort.portName);
Debug.Log(edge.outPort.portName);
newEdge.input?.Connect(newEdge);
newEdge.output?.Connect(newEdge);
AddElement(newEdge);
@ -219,7 +273,7 @@ namespace TNode.Editor.BaseViews{
public virtual void CreateInspector(){
NodeInspector nodeInspector = new NodeInspector();
this.Add(nodeInspector);
@ -227,58 +281,48 @@ namespace TNode.Editor.BaseViews{
_isInspectorOn = true;
}
public void CreateMiniMap(Rect rect){
public virtual void CreateMiniMap(Rect rect){
var miniMap = new MiniMap();
this.Add(miniMap);
miniMap.SetPosition(rect);
}
public void CreateBlackboard(){
var blackboard = new Blackboard();
public virtual void CreateBlackboard(){
_blackboard = new Blackboard();
//Blackboard add "Add Node" button
// blackboard.Add(new BlackboardSection(){
// title = "Hello World",
// });
// blackboard.addItemRequested = (item) => {
// //Create a sub window for the blackboard to show the selection
// var subWindow = ScriptableObject.CreateInstance<NodeSearchWindowProvider>();
// var subWindow = ScriptableObject.CreateNodeComponentFromGenericType<NodeSearchWindowProvider>();
// };
//
//Set black board to left side of the view
blackboard.SetPosition(new Rect(0,0,200,600));
Add(blackboard);
_blackboard.SetPosition(new Rect(0,0,200,600));
Add(_blackboard);
//Check the type of the blackboard
OnDataChanged+= (sender, e) => {
if (_data.blackboardData==null||_data.blackboardData.GetType()==typeof(BlackboardData)){
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
if(_data.blackboardData==null) return;
}
Debug.Log(_data.blackboardData);
//Iterate field of the blackboard and add a button for each field
foreach (var field in _data.blackboardData.GetType()
.GetFields(BindingFlags.Public|BindingFlags.NonPublic | BindingFlags.Instance)){
Debug.Log(field);
//if the field is MonoBehaviour,add a property field for blackboard
if(typeof(UnityEngine.Object).IsAssignableFrom(field.FieldType)){
var propertyField = new BlackboardField(null,field.Name,null){
};
blackboard.Add(propertyField);
}
if(typeof(string).IsAssignableFrom(field.FieldType)){
var propertyField = new BlackboardField(null,field.Name,null){
};
blackboard.Add(propertyField);
}
}
};
OnDataChanged+= (sender, e) => { BlackboardUpdate(); };
}
private void BlackboardUpdate(){
if (_data.blackboardData == null || _data.blackboardData.GetType() == typeof(BlackboardData)){
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
if (_data.blackboardData == null) return;
}
//Iterate field of the blackboard and add a button for each field
foreach (var field in _data.blackboardData.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)){
//if the field is MonoBehaviour,add a property field for blackboard
var propertyField = new BlackboardPropertyField(new BlackboardProperty(field.Name,field.FieldType));
_blackboard.Add(propertyField);
}
}
public virtual void DestroyInspector(){
if(_nodeInspector!=null){
this.Remove(_nodeInspector);
@ -329,11 +373,10 @@ namespace TNode.Editor.BaseViews{
}
}
private void SaveEdge(){
var links = new List<NodeLink>();
foreach (var edge in edges){
var inputNode = edge.input.node as INodeView;
var outputNode = edge.output.node as INodeView;
var links = new List<NodeLink>();
Debug.Log($"Edge{inputNode},{outputNode}");
if (inputNode != null && outputNode != null){
var inputNodeData = inputNode.GetNodeData();
var outputNodeData = outputNode.GetNodeData();
@ -347,17 +390,16 @@ namespace TNode.Editor.BaseViews{
});
links.Add(newNodeLink);
}
_data.nodeLinks = links;
}
_data.nodeLinks = links;
}
private void SaveGraphData(){
_data.NodeDictionary.Clear();
_data.nodeLinks.Clear();
SaveNode();
SaveEdge();
EditorUtility.SetDirty(_data);
}
@ -377,13 +419,16 @@ namespace TNode.Editor.BaseViews{
OnGraphViewDestroy();
}
public bool IsDroppable(){
return true;
}
public void AddTNode(NodeData nodeData, Rect rect){
if (NodeEditorExtensions.CreateNodeViewFromNodeType(nodeData.GetType()) is Node nodeView){
nodeView.SetPosition(rect);
AddElement(nodeView);
//Add a select callback to the nodeView
nodeView.RegisterCallback<MouseDownEvent>(evt => {
Debug.Log("NodeView Selected");
if (evt.clickCount == 1){
if (_isInspectorOn){
_nodeInspector.Data = nodeData;
@ -391,7 +436,6 @@ namespace TNode.Editor.BaseViews{
}
}
});
if(nodeView is INodeView nodeViewInterface){
nodeViewInterface.SetNodeData(nodeData);
}
@ -403,30 +447,27 @@ namespace TNode.Editor.BaseViews{
menu.AddItem(new GUIContent("Delete"), false, () => {
RemoveElement(nodeView);
if (nodeView is INodeView tNodeView){
var nodeData1 = tNodeView.GetNodeData();
_data.NodeDictionary.Remove(nodeData1.id);
_nodeDict.Remove(nodeData1.id);
//Break all edges connected to this node
foreach (var edge in edges){
if (edge.input.node == nodeView || edge.output.node == nodeView){
RemoveElement(edge);
}
}
Owner.graphEditorData.graphElementsData.RemoveAll(x => x.guid == nodeData1.id);
RemoveTNode(tNodeView.GetNodeData());
}
});
menu.ShowAsContext();
});
}
}
public void RemoveTNode(NodeData nodeData){
throw new NotImplementedException();
_data.NodeDictionary.Remove(nodeData.id);
var nodeView = _nodeDict[nodeData.id];
_nodeDict.Remove(nodeData.id);
//Break all edges connected to this node
foreach (var edge in edges){
if (edge.input.node == nodeView || edge.output.node == nodeView){
RemoveElement(edge);
}
}
Owner.graphEditorData.graphElementsData.RemoveAll(x => x.guid == nodeData.id);
}
}

@ -1,111 +0,0 @@
using System;
using TNode.Attribute;
using TNode.Attribute.Ports;
using TNode.Editor.Inspector;
using TNode.Models;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
namespace TNode.Editor.BaseViews{
//A NodeAttribute monitor some type of node in the graph
public abstract class NodeView<T> : Node,INodeView where T:NodeData,new(){
protected T _data;
private readonly NodeInspectorInNode _nodeInspectorInNode;
public T Data{
get => _data;
set{
if(_data!=null)
((NodeDataWrapper)_data).OnValueChanged -= OnDataValueChanged;
_data = value;
OnDataChanged?.Invoke(value);
if(_data!=null)
((NodeDataWrapper)_data).OnValueChanged += OnDataValueChanged;
}
}
private void OnDataValueChanged(NodeDataWrapper obj){
Refresh();
}
public sealed override string title{
get => base.title;
set => base.title = value;
}
public event System.Action<T> OnDataChanged;
protected NodeView(){
OnDataChanged+=OnDataChangedHandler;
_nodeInspectorInNode = new NodeInspectorInNode(){
name = "nodeInspectorInNode"
};
this.extensionContainer.Add(_nodeInspectorInNode);
}
private void OnDataChangedHandler(T obj){
this.title = _data.nodeName;
if (_nodeInspectorInNode != null){
_nodeInspectorInNode.Data = obj;
}
BuildInputAndOutputPort();
this.expanded = true;
this.RefreshExpandedState();
}
private void BuildInputAndOutputPort(){
var propertyInfos = _data.GetType().GetProperties();
foreach (var propertyInfo in propertyInfos){
var attribute = propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true);
if (attribute.Length > 0){
Port port = InstantiatePort(Orientation.Horizontal, Direction.Output,Port.Capacity.Multi,propertyInfo.PropertyType);
this.outputContainer.Add(port);
port.portName = propertyInfo.Name;
port.name = propertyInfo.Name;
}
}
foreach (var propertyInfo in propertyInfos){
Debug.Log(propertyInfos);
var attribute = propertyInfo.GetCustomAttributes(typeof(InputAttribute),true);
if (attribute.Length > 0){
Port port = InstantiatePort(Orientation.Horizontal, Direction.Input,Port.Capacity.Multi,propertyInfo.PropertyType);
this.inputContainer.Add(port);
port.portName = propertyInfo.Name;
port.name = propertyInfo.Name;
}
}
}
public void SetNodeData(NodeData nodeData){
Data = (T)nodeData;
}
public NodeData GetNodeData(){
return _data;
}
public void OnDataModified(){
Refresh();
}
public void Refresh(){
title = _data.nodeName;
}
}
public interface INodeView{
public void SetNodeData(NodeData nodeData);
public NodeData GetNodeData();
public void OnDataModified();
}
}

@ -9,14 +9,37 @@ using TNode.Editor.Inspector;
using TNode.Models;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.TestTools.Utils;
namespace TNode.Cache{
/// <summary>
/// Internal singleton class for caching TNode reflection Data.
/// </summary>
internal class NodeEditorTypeDictionary:Dictionary<Type, Type>{
//Custom camparator for sorting the dictionary by key.
private class NodeEditorTypeDictionaryComparer : IEqualityComparer<Type>
{
public bool Equals(Type x, Type y){
return x?.ToString() == y?.ToString();
}
public int GetHashCode(Type obj){
return obj.ToString().GetHashCode();
}
}
public NodeEditorTypeDictionary():base(new NodeEditorTypeDictionaryComparer()){
}
}
internal class NodeEditorSingleton{
private static NodeEditorSingleton _instance;
public readonly Dictionary<Type,Type> FromGenericToSpecific = new Dictionary<Type, Type>();
public readonly Dictionary<Type,Type> FromGenericToSpecific = new NodeEditorTypeDictionary();
public readonly Dictionary<Type, List<Type>> GraphDataUsage = new Dictionary<Type, List<Type>>();
public Dictionary<Type, Type> GraphBlackboard = new();
public static NodeEditorSingleton Instance{
@ -75,15 +98,22 @@ namespace TNode.Cache{
}
}
}
private readonly Type[] _acceptedTypesForGenericToSpecific = new Type[]{typeof(NodeView<>),typeof(DataGraphView<>),typeof(InspectorItem<>)};
private readonly Type[] _acceptedTypesForGenericToSpecific = new Type[]{typeof(NodeView<>),typeof(DataGraphView<>),typeof(InspectorItem<>),typeof(NodeView<>)};
private void SetNodeComponentAttribute(Type type){
foreach (var attribute in type.GetCustomAttributes(typeof(NodeComponentAttribute), false)){
//fetch this type 's parent class
var parent = type.BaseType;
//Check if this type is a generic type and is a generic type of NodeView or DataGraphView
if (parent is{IsGenericType: true} && _acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition())){
//Check if this type is a generic type and is a generic type of NodeView or DataGraphView,
//Two level generic definition is now supported by TNode
//Deeper nested generic definition is not supported by TNode
if (parent is{IsGenericType: true} &&
(_acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition()) ||
(parent.GetGenericTypeDefinition().IsGenericType && _acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition().GetGenericTypeDefinition()))
)
){
//Get the generic type of this type
//Add this type to the dictionary
Debug.Log($"type {type} is a registered as node component for {parent}");
FromGenericToSpecific.Add(parent, type);
}
//TODO Note that a node component only applied to a specific type of editor,so ,same GraphView could behave differently in different editor.it's a todo feature.
@ -92,32 +122,32 @@ namespace TNode.Cache{
}
//Outer wrapper for the singleton class
public static class NodeEditorExtensions{
public static T CreateInstance<T>(){
public static T CreateNodeComponentFromGenericType<T>(){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[typeof(T)];
var instance = (T)Activator.CreateInstance(implementedType);
return instance;
}
public static object CreateInstance(Type t){
public static object CreateNodeComponentFromGenericType(Type t){
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(t)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[t];
var instance = Activator.CreateInstance(implementedType);
return instance;
}
else{
Debug.Log($"No given type found {t}");
//check if t is a generic type node view
if (t is{IsGenericType: true} && t.GetGenericTypeDefinition() == typeof(NodeView<>)){
var instance = Activator.CreateInstance(typeof(NodeView<NodeData>));
return instance;
}
return null;
//check if t is a generic type node view
if (t is{IsGenericType: true} && t.GetGenericTypeDefinition() == typeof(NodeView<>)){
var instance = Activator.CreateInstance(typeof(NodeView<NodeData>));
return instance;
}
return null;
}
public static bool HasSpecificType<T>() where T : class{
public static bool HasSpecificTypeComponent<T>() where T : class{
return NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(typeof(T));
}
public static bool HasSpecificTypeComponent(Type t) {
return NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(t);
}
public static List<Type> GetGraphDataUsage(Type t){
if (NodeEditorSingleton.Instance.GraphDataUsage.ContainsKey(t)){
return NodeEditorSingleton.Instance.GraphDataUsage[t];
@ -145,13 +175,46 @@ namespace TNode.Cache{
}
public static object CreateNodeViewFromNodeType(Type t){
//Check the generic type of NodeView by t
if (t.IsGenericType){
Debug.Log($"A generic type {t} is detected");
//AKA if BlackboardDragNodeData<Camera> is pulled
//Get BlackboardDragNodeData<T> as generic type
var genericTypeDefinition = t.GetGenericTypeDefinition();
//What you want is a NodeView<BlackboardDragNodeData<T>> to be created
var genericViewType = typeof(NodeView<>).MakeGenericType(genericTypeDefinition);
Debug.Log($"The generic view type is {genericViewType}");
//search for the specific type of genericViewType in the dictionary
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(genericViewType)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[genericViewType];
//The implementedType is still a generic type ,so we make it a specific type by using MakeGenericType
Debug.Log($"{implementedType}");
//Get argument type of t
var argumentType = t.GetGenericArguments()[0];
var instance = Activator.CreateInstance(implementedType.MakeGenericType(argumentType));
return instance;
}
else{
return new DefaultNodeView();
}
}
var type = typeof(NodeView<>).MakeGenericType(t);
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(type)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[type];
var instance = Activator.CreateInstance(implementedType);
return instance;
}
else{
return new DefaultNodeView();
}

@ -0,0 +1,12 @@
using UnityEditor.Experimental.GraphView;
namespace TNode.Editor.GraphBlackboard{
public class BlackboardPropertyField:BlackboardField{
public BlackboardProperty BlackboardProperty;
public BlackboardPropertyField(BlackboardProperty blackboardProperty):base(null,blackboardProperty.PropertyName,null){
BlackboardProperty = blackboardProperty;
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5603912d8c2b4d71878f76a7eb5915a7
timeCreated: 1657033185

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 35fceca3e7c849279ed5bee0a5ee3225
timeCreated: 1657041797

@ -0,0 +1,14 @@
using System;
namespace TNode.Editor.GraphBlackboard{
public class BlackboardProperty{
public string PropertyName;
public Type PropertyType;
// public RuntimeCache.RuntimeCache.GetValueDelegate GetValue;
// public RuntimeCache.RuntimeCache.SetValueDelegate SetValue;
public BlackboardProperty(string propertyName, Type propertyType){
PropertyName = propertyName;
PropertyType = propertyType;
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b022203e15244da5bcfbf7932e7dd30c
timeCreated: 1657034160

@ -36,7 +36,7 @@ namespace TNode.Editor{
}
private void BuildGraphView(){
_graphView = NodeEditorExtensions.CreateInstance<DataGraphView<T>>();
_graphView = NodeEditorExtensions.CreateNodeComponentFromGenericType<DataGraphView<T>>();
rootVisualElement.Add(_graphView);
_graphView.StretchToParentSize();
}

@ -0,0 +1,20 @@
using System;
using TNode.Attribute;
using UnityEngine;
using UnityEngine.UIElements;
namespace TNode.Editor.Inspector.InspectorImplementation{
[NodeComponent]
public class EnumFieldItem:InspectorItem<Enum>{
public EnumFieldItem() : base(){
var field = new EnumField();
Debug.Log("An Enum Field is created");
CreateBindable(field);
OnDataChanged += () => {
field.Init(Value);
Debug.Log(Value.GetType());
};
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6eb83a1255d545e5998c7b3efd1b0d69
timeCreated: 1657193097

@ -6,8 +6,8 @@ using UnityEngine.UIElements;
namespace TNode.Editor.Inspector{
public abstract class InspectorItem<T>:VisualElement,INodeDataBinding<T> {
private NodeData _bindingNodeData;
private string _bindingFieldName;
protected NodeData _bindingNodeData;
protected string _bindingFieldName;
protected BaseField<T> Bindable;
protected event System.Action OnDataChanged;
@ -38,15 +38,19 @@ namespace TNode.Editor.Inspector{
}
private T GetValue(){
var fieldInfo = _bindingNodeData.GetType().GetField(BindingPath, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
//check field type
if (fieldInfo != null && fieldInfo.FieldType == typeof(T)){
if (fieldInfo == null){
throw new Exception("Null field info");
}
if (fieldInfo.FieldType == typeof(T)){
return (T)fieldInfo.GetValue(BindingNodeData);
}
else{
Debug.LogError("Wrong Type for current node data");
if (fieldInfo.FieldType.IsEnum){
return (T)fieldInfo.GetValue(BindingNodeData);
}
Debug.LogError("Wrong Type for current node data");
return default;
}
@ -75,7 +79,7 @@ namespace TNode.Editor.Inspector{
Bindable.UnregisterValueChangedCallback(OnInspectorItemValueChanged);
}
Bindable = bindable;
this.Add(Bindable);
Add(Bindable);
Bindable?.RegisterValueChangedCallback(OnInspectorItemValueChanged);
}
private void OnDataChangedHandler(){

@ -11,9 +11,30 @@ namespace TNode.Editor.Inspector{
public InspectorItem<T> Create<T>(){
//Check type of GraphDataType
var hasSpecificType = NodeEditorExtensions.HasSpecificType<InspectorItem<T>>();
var hasSpecificType = NodeEditorExtensions.HasSpecificTypeComponent<InspectorItem<T>>();
if (hasSpecificType){
return NodeEditorExtensions.CreateInstance<InspectorItem<T>>();
return NodeEditorExtensions.CreateNodeComponentFromGenericType<InspectorItem<T>>();
}
if (typeof(T).IsEnum){
return NodeEditorExtensions.CreateNodeComponentFromGenericType(typeof(InspectorItem<Enum>)) as InspectorItem<T>;
}
return null;
}
public INodeDataBindingBase Create(Type t){
var genericType = typeof(InspectorItem<>).MakeGenericType(t);
var hasSpecificType = NodeEditorExtensions.HasSpecificTypeComponent(genericType);
if (hasSpecificType){
return NodeEditorExtensions.CreateNodeComponentFromGenericType(genericType) as INodeDataBindingBase;
}
if (t.IsEnum){
return NodeEditorExtensions.CreateNodeComponentFromGenericType(typeof(InspectorItem<Enum>)) as INodeDataBindingBase;
}
return null;
}

@ -33,16 +33,21 @@ namespace TNode.Editor.Inspector{
if(!showInNodeViewAttribute)
continue;
//Invoke generic function Create<> of default inspector item factory to create an inspector item of appropriate type by reflection
MethodInfo methodInfo = inspectorItemFactory.GetType().GetMethod("Create", BindingFlags.Instance | BindingFlags.Public);
if (methodInfo != null){
var genericMethod = methodInfo.MakeGenericMethod(type);
var createdItem = genericMethod.Invoke(inspectorItemFactory,null) as VisualElement;
Add(createdItem);
if (createdItem is INodeDataBindingBase castedItem){
castedItem.BindingNodeData = _data;
castedItem.BindingPath = bindingPath;
}
var createdItem = inspectorItemFactory.Create(type);
if (createdItem is { } castedItem){
castedItem.BindingNodeData = _data;
castedItem.BindingPath = bindingPath;
}
Add((VisualElement)createdItem);
// MethodInfo methodInfo = inspectorItemFactory.GetType().GetMethod("Create", BindingFlags.Instance | BindingFlags.Public);
// if (methodInfo != null){
// var genericMethod = methodInfo.MakeGenericMethod(type);
// Debug.Log(genericMethod);
// var createdItem = genericMethod.Invoke(inspectorItemFactory,null) as VisualElement;
// Add(createdItem);
// Debug.Log(createdItem?.GetType());
//
// }
}
}
}

@ -1,13 +0,0 @@
using TNode.Models;
using UnityEngine.UIElements;
namespace TNode.Editor.Inspector{
/// <summary>
/// Tiny Inspector item is a simple inspector item inside a node view ,it monitor node data
///
/// </summary>
public class TinyInspectorItem<T>:VisualElement,INodeDataBinding<T>{
public string BindingPath{ get; set; }
public NodeData BindingNodeData{ get; set; }
}
}

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7eeddeb28a5a475c85c85ceae3198778
timeCreated: 1656142058

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 400542f3cec140e2b55e2bcf637b2d9b
timeCreated: 1657009018

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea202e63fe8e4d3b9b7759488419dde7
timeCreated: 1657197080

@ -0,0 +1,15 @@
using TNode.Attribute;
using TNode.Editor.BaseViews;
using TNode.Models;
namespace TNode.Editor.NodeViews{
[NodeComponent]
public class DragNodeView<T>:NodeView<BlackboardDragNodeData<T>>{
public DragNodeView() : base(){
//Make capsule like style
this.titleContainer.visible = false;
this.titleContainer.RemoveFromHierarchy();
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 899b964a5f674c2fbf1db20cf40ff5e7
timeCreated: 1657197096

@ -0,0 +1,179 @@
using System;
using System.Linq;
using System.Reflection;
using TNode.Attribute;
using TNode.Attribute.Ports;
using TNode.Editor.Inspector;
using TNode.Models;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace TNode.Editor.BaseViews{
//A NodeAttribute monitor some type of node in the graph
public abstract class NodeView<T> : Node,INodeView where T:NodeData,new(){
protected T _data;
private readonly NodeInspectorInNode _nodeInspectorInNode;
public T Data{
get => _data;
set{
if(_data!=null)
((NodeDataWrapper)_data).OnValueChanged -= OnDataValueChanged;
_data = value;
OnDataChanged?.Invoke(value);
if(_data!=null)
((NodeDataWrapper)_data).OnValueChanged += OnDataValueChanged;
}
}
private void OnDataValueChanged(NodeDataWrapper obj){
Refresh();
}
public sealed override string title{
get => base.title;
set => base.title = value;
}
public event System.Action<T> OnDataChanged;
protected NodeView(){
OnDataChanged+=OnDataChangedHandler;
_nodeInspectorInNode = new NodeInspectorInNode(){
name = "nodeInspectorInNode"
};
this.extensionContainer.Add(_nodeInspectorInNode);
BuildDoubleClickRename();
}
private void OnDataChangedHandler(T obj){
this.title = _data.nodeName;
if (_nodeInspectorInNode != null){
_nodeInspectorInNode.Data = obj;
}
BuildInputAndOutputPort();
this.expanded = true;
this.RefreshExpandedState();
}
protected virtual string BuildPortName(PortAttribute portAttribute,PropertyInfo propertyInfo,params object[] args){
switch (portAttribute.NameHandling){
case PortNameHandling.Auto:
return portAttribute.Name.Trim(' ').Length>0?portAttribute.Name:propertyInfo.Name;
break;
case PortNameHandling.Manual:
return portAttribute.Name;
break;
case PortNameHandling.MemberName:
return propertyInfo.Name;
case PortNameHandling.Format:
return String.Format(propertyInfo.Name, args);
case PortNameHandling.MemberType:
return propertyInfo.PropertyType.Name;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// of course you can override this method to build your own port builder
/// </summary>
protected virtual void BuildInputAndOutputPort(){
var propertyInfos = _data.GetType().GetProperties();
foreach (var propertyInfo in propertyInfos){
if (propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true).FirstOrDefault() is OutputAttribute attribute){
Port port = InstantiatePort(Orientation.Horizontal, Direction.Output,Port.Capacity.Multi,propertyInfo.PropertyType);
this.outputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName;
port.name = portName;
}
}
foreach (var propertyInfo in propertyInfos){
if(propertyInfo.GetCustomAttributes(typeof(InputAttribute),true).FirstOrDefault() is InputAttribute attribute){
Port port = InstantiatePort(Orientation.Horizontal, Direction.Input,Port.Capacity.Single,propertyInfo.PropertyType);
this.inputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName;
port.name = portName;
}
}
}
public void StartARenameTitleTextField(){
var textField = new TextField{
value = title,
style ={
//Make the text filed overlap the title container
position = Position.Absolute,
left = 0,
top = 0,
width = titleContainer.layout.width,
height = titleContainer.layout.height
}
};
textField.StretchToParentSize();
textField.RegisterValueChangedCallback(evt2 => {
title = evt2.newValue;
});
textField.RegisterCallback<FocusOutEvent>(evt2 => {
title = textField.text;
((NodeDataWrapper)_data).SetValue("nodeName",textField.text);
textField.RemoveFromHierarchy();
});
//if enter is pressed ,set the title and remove the text field
textField.RegisterCallback<KeyDownEvent>(evt2 => {
if (evt2.keyCode == KeyCode.Return){
title = textField.text;
((NodeDataWrapper)_data).SetValue("nodeName",textField.text);
textField.RemoveFromHierarchy();
}
});
titleContainer.Add(textField);
textField.Focus();
}
private void BuildDoubleClickRename(){
//when double click titleContainer ,create a textfield to rename the node
titleContainer.RegisterCallback<MouseDownEvent>(evt => {
if (evt.clickCount == 2){
StartARenameTitleTextField();
}
});
}
public void SetNodeData(NodeData nodeData){
Data = (T)nodeData;
}
public NodeData GetNodeData(){
return _data;
}
public void OnDataModified(){
Refresh();
}
public void Refresh(){
title = _data.nodeName;
}
}
public interface INodeView{
public void SetNodeData(NodeData nodeData);
public NodeData GetNodeData();
public void OnDataModified();
}
}

@ -1,5 +1,16 @@
namespace TNode.Editor{
public class BlackboardSearchWindowProvider{
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
namespace TNode.Editor{
public class BlackboardSearchWindowProvider:ISearchWindowProvider{
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context){
throw new System.NotImplementedException();
}
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context){
throw new System.NotImplementedException();
}
}
}

@ -48,8 +48,8 @@ namespace TNode.Editor{
//Check if type is derived from NodeData
if (typeof(NodeData).IsAssignableFrom(type)){
//Make an instance of the type
if (NodeHelper.InstantiateNodeData(type) is { } nodeData){
nodeData.nodeName = "New Node";
if (NodeCreator.InstantiateNodeData(type) is { } nodeData){
nodeData.nodeName = $"New {type.Name}";
((IDataGraphView) _graphView).AddTNode(nodeData, new Rect(localPos.x, localPos.y, 100, 100));
}
}

@ -2,7 +2,7 @@
using TNode.Models;
namespace TNode.Editor.Tools.NodeCreator{
public static class NodeHelper{
public static class NodeCreator{
/// <summary>
/// always use this to create a new node.

@ -21,7 +21,9 @@ namespace TNode.JsonSerialize{
DateFormatString = "yyyy-MM-dd HH:mm:ss",
Converters = new List<JsonConverter> { new Vector3Converter() },
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = new WritablePropertiesOnlyResolver()
ContractResolver = new WritablePropertiesOnlyResolver(),
Formatting = Formatting.Indented
};

@ -1,19 +0,0 @@
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using TNode.Attribute.Ports;
namespace TNode.Models{
public class BlackDragNodeData<T>:NodeData{
[JsonIgnore]
private string _blackDragData;
[JsonIgnore]
private BlackboardData _blackboardData;
[Output] public T value => _blackboardData.GetValue<T>(_blackDragData);
public BlackDragNodeData(string blackDragData,BlackboardData blackboardData){
_blackDragData = blackDragData;
_blackboardData = blackboardData;
}
}
}

@ -1,8 +1,6 @@
namespace TNode.Models{
public class BlackboardData:IModel{
public T GetValue<T>(string key){
return default(T);
}
}
}

@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using TNode.Attribute;
using TNode.Attribute.Ports;
using TNode.RuntimeCache;
namespace TNode.Models{
public class BlackboardDragNodeData<T>:NodeData{
private string _blackDragData;
[JsonIgnore]
private BlackboardData _blackboardData;
[Output("",PortNameHandling.MemberType)]
public T Value => _blackboardData.GetValue<T>(_blackDragData);
public BlackboardDragNodeData(){
}
}
}

@ -33,7 +33,6 @@ namespace TNode.Models{
var deserializedBlackboard = JsonConvert.DeserializeObject<BlackboardData>(jsonBlackboard,JsonSerializeTool.JsonSerializerSettings);
blackboardData = deserializedBlackboard;
Debug.Log("hi");
}
}
}

@ -0,0 +1,10 @@
using System.Collections.Generic;
using TNode.Models;
namespace TNode.Runtime{
public class RuntimeBlackboard<T> where T:BlackboardData{
public T Data { get; set; }
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b69b2dedba24e35bf04bc9112da6b12
timeCreated: 1656958407

@ -15,39 +15,15 @@ namespace TNode.RuntimeCache{
}
//delegate return a value from a nodedata
public delegate object GetValueDelegate(IModel nodeData);
public delegate object SetValueDelegate(IModel nodeData,object value);
public readonly Dictionary<Type, List<GetValueDelegate>> CachedDelegatesForGettingValue =
public readonly Dictionary<Type, Dictionary<string,GetValueDelegate>> CachedDelegatesForGettingValue =
new ();
public void ExecuteOutput<T>(T nodeData) where T:NodeData{
var type = typeof(T);
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
return;
}
var delegates = CachedDelegatesForGettingValue[type];
foreach(var delegateInstance in delegates){
var value = delegateInstance(nodeData);
}
}
private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"};
public void RegisterRuntimeNode<T>() where T:NodeData{
var type = typeof(T);
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new List<GetValueDelegate>());
var properties = type.GetProperties();
foreach(var property in properties){
var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(getValueDelegate);
}
}
else{
//Cache already exists for this type
}
}
public void RegisterRuntimeBlackboard(Type type){
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new List<GetValueDelegate>());
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
var properties = type.GetProperties();
foreach(var property in properties){
//if the property only has a setter ,skip
@ -55,14 +31,46 @@ namespace TNode.RuntimeCache{
continue;
}
var getValueDelegate = GetValueDelegateForProperty(property);
CachedDelegatesForGettingValue[type].Add(getValueDelegate);
CachedDelegatesForGettingValue[type].Add(property.Name,getValueDelegate);
}
//register the fields
var fields = type.GetFields();
foreach(var field in fields){
var getValueDelegate = GetValueDelegateForField(field);
CachedDelegatesForGettingValue[type].Add(field.Name,getValueDelegate);
}
}
}
private GetValueDelegate GetValueDelegateForField(FieldInfo field){
return field.GetValue;
}
private GetValueDelegate GetValueDelegateForProperty(PropertyInfo property){
var getValueDelegate = (GetValueDelegate)Delegate.CreateDelegate(typeof(GetValueDelegate), property.GetGetMethod());
return getValueDelegate;
}
}
public static class RuntimeExtension{
//todo latter on i will try some way caching reflection more efficiently
public static T GetValue<T>(this BlackboardData blackboardData,string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
return (T) method.Invoke(blackboardData);
}
public static object GetValue(this BlackboardData blackboardData, string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
return method.Invoke(blackboardData);
}
public static RuntimeCache.GetValueDelegate GetValueDelegate(this BlackboardData blackboardData,string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
return method;
}
}
}
Loading…
Cancel
Save