diff --git a/README.md b/README.md index 73c46e9..20868e5 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # T-Node Node graph creation tool based on unity experimental graphview and if possible latter,GTF. + 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 . -and it's mainly for my own use now. The tool separate its graph editor implementation and the graph creation logic. @@ -12,25 +12,24 @@ The tool separate its graph editor implementation and the graph creation logic. currently under development -# Main Features +# Some Features -* Create graph script by the creator tool +* Create graph script a the creator tool * Node creation based on specified type of graph * 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 # Some To-dos -* Caching runtime state for faster execution -* Undo redo support * Function as port -* Better blackboard support +* Circular dependency support for some situations such as FSM +* Edge colors customization # Usage -No ,It's better not to use TNode at current stage until it's stable. - +Not yet documented ### Convention diff --git a/TNode/TNodeCore/Runtime/Attributes/GraphUsageAttribute.cs b/TNode/TNodeCore/Runtime/Attributes/GraphUsageAttribute.cs index c58a82b..9b29815 100644 --- a/TNode/TNodeCore/Runtime/Attributes/GraphUsageAttribute.cs +++ b/TNode/TNodeCore/Runtime/Attributes/GraphUsageAttribute.cs @@ -4,7 +4,7 @@ using TNodeCore.Runtime.Models; namespace TNodeCore.Runtime.Attributes{ /// - /// Use this attribute to claim the usage of a type derived IModel IModel + /// Use this attribute to claim the usage of a type derived Model Model /// it can be applied to the same node multiple times. /// /// [GraphUsage(DialogueGraph)] diff --git a/TNode/TNodeCore/Runtime/Models/BlackboardData.cs b/TNode/TNodeCore/Runtime/Models/BlackboardData.cs index a5d95a9..4d67b50 100644 --- a/TNode/TNodeCore/Runtime/Models/BlackboardData.cs +++ b/TNode/TNodeCore/Runtime/Models/BlackboardData.cs @@ -1,4 +1,5 @@ using System; +using UnityEngine; namespace TNodeCore.Runtime.Models{ /// @@ -6,11 +7,10 @@ namespace TNodeCore.Runtime.Models{ /// [Serializable] - public class BlackboardData:IModel,ICloneable{ - public object Clone(){ - return this.MemberwiseClone(); - } + public class BlackboardData:Model,ICloneable{ - + + + } } \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Models/IModel.cs b/TNode/TNodeCore/Runtime/Models/IModel.cs deleted file mode 100644 index c4ea9e9..0000000 --- a/TNode/TNodeCore/Runtime/Models/IModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace TNodeCore.Runtime.Models{ - public interface IModel:ICloneable{ - - } -} \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Models/Model.cs b/TNode/TNodeCore/Runtime/Models/Model.cs new file mode 100644 index 0000000..06b99dc --- /dev/null +++ b/TNode/TNodeCore/Runtime/Models/Model.cs @@ -0,0 +1,17 @@ +using System; +using UnityEngine; +using UnityEngine.Serialization; + +namespace TNodeCore.Runtime.Models{ + [Serializable] + public abstract class Model:ICloneable{ + #if UNITY_EDITOR + public Rect positionInView; + #endif + + public object Clone(){ + var memberwiseClone = this.MemberwiseClone(); + return memberwiseClone; + } + } +} \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Models/IModel.cs.meta b/TNode/TNodeCore/Runtime/Models/Model.cs.meta similarity index 100% rename from TNode/TNodeCore/Runtime/Models/IModel.cs.meta rename to TNode/TNodeCore/Runtime/Models/Model.cs.meta diff --git a/TNode/TNodeCore/Runtime/Models/NodeData.cs b/TNode/TNodeCore/Runtime/Models/NodeData.cs index 7fc1ae0..f685a13 100644 --- a/TNode/TNodeCore/Runtime/Models/NodeData.cs +++ b/TNode/TNodeCore/Runtime/Models/NodeData.cs @@ -12,7 +12,7 @@ namespace TNodeCore.Runtime.Models{ /// /// [Serializable] - public class NodeData:IModel{ + public class NodeData:Model{ public NodeData() : base(){ //Object Registration @@ -37,8 +37,7 @@ namespace TNodeCore.Runtime.Models{ } #endif - public object Clone(){ - return this.MemberwiseClone(); - } + + } } \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs b/TNode/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs index 16636a7..c2db650 100644 --- a/TNode/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs +++ b/TNode/TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs @@ -64,8 +64,8 @@ namespace TNodeCore.Runtime.RuntimeCache{ get{ return _instance ??= new RuntimeCache(); } } //delegate return a value from a nodedata - public delegate object GetValueDelegate(IModel nodeData); - public delegate void SetValueDelegate(IModel nodeData,object value); + public delegate object GetValueDelegate(Model nodeData); + public delegate void SetValueDelegate(Model nodeData,object value); @@ -258,11 +258,11 @@ namespace TNodeCore.Runtime.RuntimeCache{ public static class RuntimeExtension{ //todo latter on i will try some way caching reflection more efficiently - public static T GetValue(this IModel data,string path,Type type=null){ + public static T GetValue(this Model data,string path,Type type=null){ var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[type??data.GetType()][path]; return (T) method.Invoke(data); } - public static object GetValue(this IModel data, string path,Type type=null){ + public static object GetValue(this Model data, string path,Type type=null){ if(!RuntimeCache.Instance.CachedDelegatesForGettingValue.ContainsKey(type??data.GetType())){ return null; } @@ -270,7 +270,7 @@ namespace TNodeCore.Runtime.RuntimeCache{ var method = dic.ContainsKey(path) ? dic[path] : null; return method?.Invoke(data); } - public static object GetListValue(this IModel data,string path,int index,Type type=null){ + public static object GetListValue(this Model data,string path,int index,Type type=null){ if(!RuntimeCache.Instance.CachedDelegatesForGettingValue.ContainsKey(type??data.GetType())){ return null; } @@ -286,11 +286,11 @@ namespace TNodeCore.Runtime.RuntimeCache{ return list[index]; } - public static void SetValue(this IModel data,string path,T value,Type type=null){ + public static void SetValue(this Model data,string path,T value,Type type=null){ var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path]; method.Invoke(data,value); } - public static void SetValue(this IModel data,string path,object value,Type type=null){ + public static void SetValue(this Model data,string path,object value,Type type=null){ var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path]; method.Invoke(data,value); } diff --git a/TNode/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs b/TNode/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs index 5947bfc..598fe9d 100644 --- a/TNode/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs +++ b/TNode/TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs @@ -89,7 +89,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.Cache{ private void SetGraphUsageAttribute(Type type){ foreach (var attribute in type.GetCustomAttributes(typeof(GraphUsageAttribute), true)){ var parent = type.BaseType; - if (typeof(IModel).IsAssignableFrom(type.BaseType)){ + if (typeof(Model).IsAssignableFrom(type.BaseType)){ //Check if GraphDataUsage dictionary has GraphDataType of attribute if (typeof(NodeData).IsAssignableFrom(type)){ diff --git a/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs b/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs index a595919..e6929fe 100644 --- a/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs +++ b/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs @@ -98,15 +98,14 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ var res = DragAndDrop.objectReferences; foreach (var obj in res){ if (obj is T graphData){ - Data = graphData; IsRuntimeGraph = false; + Data = graphData; } else{ if (obj is GameObject gameObject){ if (gameObject.GetComponent() != null){ if (gameObject.GetComponent().graphData != null){ - _runtimeGraph = gameObject.GetComponent(); IsRuntimeGraph = true; BuildRuntimeGraphBehaviour(); @@ -114,7 +113,6 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ if(Data==null){ Debug.LogError($"Dragged a wrong graph data to editor,expected {typeof(T)} but got {gameObject.GetComponent().graphData.GetType()}"); } - } } } @@ -341,8 +339,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ } private void AddPersistentNode(NodeData dataNode){ - var nodePos = Owner.graphEditorData.graphElementsData.FirstOrDefault(x => x.guid == dataNode.id)?.pos ?? - new Rect(0, 0, 200, 200); + var nodePos = dataNode.positionInView; AddTNode(dataNode, nodePos); } //OnDataChanged event @@ -391,21 +388,21 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ } public void SaveEditorData(GraphEditorData graphEditorData){ - graphEditorData.graphElementsData?.Clear(); - //iterator nodes - if (graphEditorData.graphElementsData == null){ - graphEditorData.graphElementsData = new List(); - } - foreach (var node in this.nodes){ - var nodeEditorData = new GraphElementEditorData{ - pos = node.GetPosition(), - }; - if (node is IBaseNodeView nodeView){ - nodeEditorData.guid = nodeView.GetNodeData().id; - } - graphEditorData.graphElementsData.Add(nodeEditorData); - EditorUtility.SetDirty(graphEditorData); - } + // graphEditorData.graphElementsData?.Clear(); + // //iterator nodes + // if (graphEditorData.graphElementsData == null){ + // graphEditorData.graphElementsData = new List(); + // } + // foreach (var node in this.nodes){ + // var nodeEditorData = new GraphElementEditorData{ + // pos = node.GetPosition(), + // }; + // if (node is IBaseNodeView nodeView){ + // nodeEditorData.guid = nodeView.GetNodeData().id; + // } + // graphEditorData.graphElementsData.Add(nodeEditorData); + // EditorUtility.SetDirty(graphEditorData); + // } } public void SaveWithEditorData(GraphEditorData graphEditorData){ @@ -499,8 +496,15 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ #region implement interfaces public void AddTNode(NodeData nodeData, Rect rect){ if (NodeEditorExtensions.CreateNodeViewFromNodeType(nodeData.GetType()) is Node nodeView){ - nodeView.SetPosition(rect); + //convert rect at graph space + + var resPos = this.viewTransform.matrix.inverse.MultiplyPoint3x4(rect.position); + rect.position = resPos; + if(nodeView is IBaseNodeView nodeViewInterface){ + nodeViewInterface.SetNodeData(nodeData); + } AddElement(nodeView); + ((IBaseNodeView)nodeView).InitializePosition(rect); //Add a select callback to the nodeView nodeView.RegisterCallback(evt => { if (evt.clickCount == 1){ @@ -510,14 +514,17 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ } } }); - if(nodeView is IBaseNodeView nodeViewInterface){ - nodeViewInterface.SetNodeData(nodeData); - } - _nodeDict.Add(nodeData.id, nodeView); + + + + if(_nodeDict.ContainsKey(nodeData.id)==false) + _nodeDict.Add(nodeData.id, nodeView); if (_data.NodeDictionary.ContainsKey(nodeData.id) == false){ Undo.RegisterCompleteObjectUndo(_data,"Node Creation"); _data.NodeDictionary.Add(nodeData.id,nodeData); } + + //register an callback ,when right click context menu nodeView.RegisterCallback(evt => { var menu = new GenericMenu(); @@ -558,6 +565,13 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ } public bool TestMode{ get; set; } + + public override EventPropagation DeleteSelection(){ + Undo.RegisterCompleteObjectUndo(_data,"Delete Selection"); + var res = base.DeleteSelection(); + ResetGraphView(); + return res; + } public void CreateBlackboard(){ _blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T)); diff --git a/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs b/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs index c36a3b1..ca5ddfc 100644 --- a/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs +++ b/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs @@ -184,6 +184,18 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ Refresh(); } + 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; + base.SetPosition(newPos); + } + + public void InitializePosition(Rect pos){ + base.SetPosition(pos); + } + public void Refresh(){ title = _data.nodeName; } @@ -195,6 +207,8 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ public void OnDataModified(); IBaseDataGraphView BaseDataGraphView{ get; } + + public void InitializePosition(Rect pos); } public interface INodeView:IBaseNodeView where T:NodeData,new(){ diff --git a/TNode/TNodeGtfImpl/Editor.meta b/TNode/TNodeGtfImpl/Editor.meta new file mode 100644 index 0000000..802b624 --- /dev/null +++ b/TNode/TNodeGtfImpl/Editor.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fa7dcd53ede6483793978d44af911270 +timeCreated: 1659437565 \ No newline at end of file