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/Editor/EditorPersistence/GraphEditorData.cs b/TNode/TNodeCore/Editor/EditorPersistence/GraphEditorData.cs index 7d85841..10414d7 100644 --- a/TNode/TNodeCore/Editor/EditorPersistence/GraphEditorData.cs +++ b/TNode/TNodeCore/Editor/EditorPersistence/GraphEditorData.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using TNodeCore.Editor.NodeGraphView; using TNodeCore.Runtime.Models; using UnityEngine; +using UnityEngine.Serialization; namespace TNodeCore.Editor.EditorPersistence{ @@ -14,7 +15,8 @@ namespace TNodeCore.Editor.EditorPersistence{ public GraphImplType graphImplType = GraphImplType.GraphViewImpl; public static Func GraphViewImplCreator; public static Func GtfImplCreator; - + [FormerlySerializedAs("testMode")] public bool autoUpdate; + public IDataGraphView GetGraphView () where T:GraphData{ switch (graphImplType){ case GraphImplType.GraphViewImpl:{ diff --git a/TNode/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs b/TNode/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs index d907b4b..ee45cfc 100644 --- a/TNode/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs +++ b/TNode/TNodeCore/Editor/NodeGraphView/IBaseDataGraphView.cs @@ -14,7 +14,7 @@ namespace TNodeCore.Editor.NodeGraphView{ public void RemoveLink(NodeLink nodeLink); - public bool TestMode{ get; set; } + public bool AutoUpdate{ get; set; } public void CreateBlackboard(); public GraphData GetGraphData(); public BlackboardData GetBlackboardData(); @@ -30,9 +30,12 @@ namespace TNodeCore.Editor.NodeGraphView{ public void SetGraphData(GraphData graph); - void NotifyRuntimeUpdate(); - public Action AfterRuntimeGraphUpdate{ get; set; } + public Action AfterGraphResolved{ get; set; } void AfterEditorLoadGraphView(); + + //todo remove it later ,keep it now + void NotifyRuntimeUpdate(); + } } \ No newline at end of file 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/Attributes/HideInBlackboard.cs b/TNode/TNodeCore/Runtime/Attributes/HideInBlackboard.cs new file mode 100644 index 0000000..b7268ce --- /dev/null +++ b/TNode/TNodeCore/Runtime/Attributes/HideInBlackboard.cs @@ -0,0 +1,6 @@ +namespace TNodeCore.Runtime.Attributes{ + + public class HideInBlackboard:System.Attribute{ + + } +} \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/HideInBlackboard.cs.meta b/TNode/TNodeCore/Runtime/Attributes/HideInBlackboard.cs.meta new file mode 100644 index 0000000..5daa766 --- /dev/null +++ b/TNode/TNodeCore/Runtime/Attributes/HideInBlackboard.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 731893f9f7824b939cee169d61092ad3 +timeCreated: 1659517588 \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs b/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs new file mode 100644 index 0000000..ae7106c --- /dev/null +++ b/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs @@ -0,0 +1,5 @@ +namespace TNodeCore.Runtime.Attributes{ + public class ModelColor{ + + } +} \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs.meta b/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs.meta new file mode 100644 index 0000000..e0cce46 --- /dev/null +++ b/TNode/TNodeCore/Runtime/Attributes/ModelColor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cdec5767969f4e11bbcfb245693796f9 +timeCreated: 1659521617 \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/Ports/InputAttribute.cs b/TNode/TNodeCore/Runtime/Attributes/Ports/InputAttribute.cs index 6b8ba37..676f6db 100644 --- a/TNode/TNodeCore/Runtime/Attributes/Ports/InputAttribute.cs +++ b/TNode/TNodeCore/Runtime/Attributes/Ports/InputAttribute.cs @@ -1,5 +1,6 @@ using System; using JetBrains.Annotations; +using UnityEngine; namespace TNodeCore.Runtime.Attributes.Ports{ [MeansImplicitUse] @@ -7,5 +8,7 @@ namespace TNodeCore.Runtime.Attributes.Ports{ public class InputAttribute : PortAttribute{ public InputAttribute(string name="", PortNameHandling nameHandling = PortNameHandling.Auto,TypeHandling typeHandling=TypeHandling.Declared) : base(name, nameHandling,typeHandling){ } + public InputAttribute(Color color):base(color){ + } } } \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/Ports/OutputAttribute.cs b/TNode/TNodeCore/Runtime/Attributes/Ports/OutputAttribute.cs index b11340d..47eb6c9 100644 --- a/TNode/TNodeCore/Runtime/Attributes/Ports/OutputAttribute.cs +++ b/TNode/TNodeCore/Runtime/Attributes/Ports/OutputAttribute.cs @@ -1,8 +1,14 @@ -namespace TNodeCore.Runtime.Attributes.Ports{ +using UnityEngine; + +namespace TNodeCore.Runtime.Attributes.Ports{ public class OutputAttribute:PortAttribute{ public OutputAttribute(string name="", PortNameHandling nameHandling = PortNameHandling.Auto,TypeHandling typeHandling = TypeHandling.Declared) : base(name, nameHandling,typeHandling){ } + public OutputAttribute(Color color):base(color){ + } + + } } \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/Attributes/Ports/PortAttribute.cs b/TNode/TNodeCore/Runtime/Attributes/Ports/PortAttribute.cs index b27710e..9146a79 100644 --- a/TNode/TNodeCore/Runtime/Attributes/Ports/PortAttribute.cs +++ b/TNode/TNodeCore/Runtime/Attributes/Ports/PortAttribute.cs @@ -1,6 +1,8 @@ using System; +using UnityEngine; using JetBrains.Annotations; + namespace TNodeCore.Runtime.Attributes.Ports{ public enum PortNameHandling{ @@ -23,6 +25,7 @@ namespace TNodeCore.Runtime.Attributes.Ports{ public readonly PortNameHandling NameHandling; public Type HandledType; public bool Multiple = true; + public Color PortColor = Color.black; public TypeHandling TypeHandling{ get; set; } public PortAttribute(string name,PortNameHandling nameHandling=PortNameHandling.Auto,TypeHandling typeHandling=TypeHandling.Declared){ this.Name = name; @@ -30,6 +33,11 @@ namespace TNodeCore.Runtime.Attributes.Ports{ this.TypeHandling = typeHandling; } + public PortAttribute(Color color):this("",PortNameHandling.Auto,TypeHandling.Declared){ + + PortColor = color; + } + } } \ No newline at end of file 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..6571aa0 --- /dev/null +++ b/TNode/TNodeCore/Runtime/Models/Model.cs @@ -0,0 +1,19 @@ +using System; +using TNodeCore.Runtime.Attributes; +using UnityEngine; +using UnityEngine.Serialization; + +namespace TNodeCore.Runtime.Models{ + [Serializable] + public abstract class Model:ICloneable{ + #if UNITY_EDITOR + [HideInBlackboard] + 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/NodeLogger.cs b/TNode/TNodeCore/Runtime/NodeLogger.cs new file mode 100644 index 0000000..d967e62 --- /dev/null +++ b/TNode/TNodeCore/Runtime/NodeLogger.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using TNodeCore.Runtime.Models; +using UnityEngine; + +namespace TNodeCore.Runtime{ + public static class NodeLogger{ + public static Dictionary Loggers = new (); + + public static void Log(this NodeData t,string message){ + if (!Loggers.ContainsKey(t.id)) return; + var nodeLoggerImpl = Loggers[t.id]; + nodeLoggerImpl.Log(message); + Debug.Log(message); + + } + } + + public interface INodeLoggerImpl{ + public void Log(string message); + } +} \ No newline at end of file diff --git a/TNode/TNodeCore/Runtime/NodeLogger.cs.meta b/TNode/TNodeCore/Runtime/NodeLogger.cs.meta new file mode 100644 index 0000000..1d16c7b --- /dev/null +++ b/TNode/TNodeCore/Runtime/NodeLogger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 48be07a2cf064167b15d378ac41757c4 +timeCreated: 1659592209 \ 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/GraphBlackboard/DefaultGraphBlackboardView.cs b/TNode/TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs index 36bd89c..ac89773 100644 --- a/TNode/TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs +++ b/TNode/TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs @@ -6,6 +6,7 @@ using TNodeCore.Editor.NodeGraphView; using TNodeCore.Editor.Serialization; using TNodeCore.Runtime.Attributes; using TNodeCore.Runtime.Models; +using Unity.VisualScripting; using UnityEditor; using UnityEditor.Experimental.GraphView; using UnityEditor.UIElements; @@ -22,8 +23,8 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{ } protected override void UpdateBlackboard(BlackboardData data){ + Clear(); if (data == null) return; - this.Clear(); var serializedObject = new SerializedObject((BlackboardDataWrapper)data); var currentGraphView = graphView as IBaseDataGraphView; var isRuntimeGraph = currentGraphView?.IsRuntimeGraph ?? false; @@ -33,6 +34,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.GraphBlackboard{ Add(blackboardGlobalSection); foreach (var field in data.GetType() .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)){ + if(field.HasAttribute(typeof(HideInBlackboard))) continue; //if the field is MonoBehaviour,add a property field for blackboard //skip if the field is a list or Ilist if (!typeof(IList).IsAssignableFrom(field.FieldType)&&!field.FieldType.IsArray){ diff --git a/TNode/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs b/TNode/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs index f54a4c6..66eb4f9 100644 --- a/TNode/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs +++ b/TNode/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs @@ -67,7 +67,7 @@ namespace TNode.TNodeGraphViewImpl.Editor.Inspector{ Add(drawer); } - var globalTest = GetFirstAncestorOfType()?.TestMode; + var globalTest = GetFirstAncestorOfType()?.AutoUpdate; if(globalTest??false){ CreateTestButton(); } diff --git a/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs b/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs index a595919..8a1459d 100644 --- a/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs +++ b/TNode/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using TNode.TNodeGraphViewImpl.Editor.Cache; using TNode.TNodeGraphViewImpl.Editor.GraphBlackboard; using TNode.TNodeGraphViewImpl.Editor.Inspector; @@ -16,6 +17,7 @@ using TNodeCore.Editor.Tools.NodeCreator; using TNodeCore.Runtime.Components; using TNodeCore.Runtime.Models; using TNodeCore.Runtime.RuntimeCache; +using Unity.VisualScripting; using UnityEditor; using UnityEditor.Experimental.GraphView; using UnityEngine; @@ -25,16 +27,18 @@ using Edge = UnityEditor.Experimental.GraphView.Edge; namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ public class BaseDataGraphView:GraphView,IDataGraphView where T:GraphData{ + #region const + public const float RefreshRate = 1f; + #endregion #region variables and properties private T _data; private RuntimeGraph _runtimeGraph; - private bool _isInspectorOn; private NodeSearchWindowProvider _nodeSearchWindowProvider; private NodeInspector _nodeInspector; private Dictionary _nodeDict = new(); private IBlackboardView _blackboard; - private bool _runtimeGraphUpdate; + private bool _loaded; public T Data{ get{ return _data; } set{ @@ -98,15 +102,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 +117,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()}"); } - } } } @@ -147,25 +149,36 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ } private void BuildRuntimeGraphBehaviour(){ - EditorApplication.update+= UpdateRuntimeGraphBehaviour; + //EditorApplication.update+= UpdateRuntimeGraphBehaviour; + UpdateRuntimeGraphBehaviourInTime(); } - - private void UpdateRuntimeGraphBehaviour(){ - if(_runtimeGraph != null){ - if (_runtimeGraphUpdate){ - _runtimeGraphUpdate = false; - _runtimeGraph.ResolveDependency(); - - AfterRuntimeGraphUpdate?.Invoke(); + + private async void UpdateRuntimeGraphBehaviourInTime(){ + + while (_loaded){ + await Task.Delay(TimeSpan.FromSeconds(RefreshRate)); + if(_runtimeGraph != null){ + if (AutoUpdate){ + _runtimeGraph.ResolveDependency(); + AfterGraphResolved?.Invoke(); + } } - - - } - else{ - EditorApplication.update -= UpdateRuntimeGraphBehaviour; } } + // private void UpdateRuntimeGraphBehaviour(){ + // if(_runtimeGraph != null){ + // if (_runtimeGraphUpdate){ + // _runtimeGraphUpdate = false; + // _runtimeGraph.ResolveDependency(); + // + // AfterGraphResolved?.Invoke(); + // } + // } + // else{ + // EditorApplication.update -= UpdateRuntimeGraphBehaviour; + // } + // } private void CheckDataAfterInit(){ if(Data == null){ @@ -201,6 +214,18 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ OnGraphViewCreate(); BuildUndo(); + + _loaded = true; + + SetDetachedFromPanel(); + + + } + + private void SetDetachedFromPanel(){ + this.RegisterCallback(evt => { + _loaded = false; + }); } private void BuildUndo(){ @@ -227,22 +252,32 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ //Add a toggle button to toggle test mode - var testModeToggle = new Toggle{ + var autoUpdateToggle = new Toggle{ name = "TestModeToggle", label = "Test Mode", - value = false + value = AutoUpdate }; - testModeToggle.RegisterValueChangedCallback(evt => { + autoUpdateToggle.RegisterValueChangedCallback(evt => { if (evt.newValue){ - TestMode = true; + AutoUpdate = true; } else{ - TestMode = false; + AutoUpdate = false; } }); - visualElement.Add(testModeToggle); + visualElement.Add(autoUpdateToggle); - + var runButton = new Button{ + name = "RunButton", + text = "Run Once" + }; + runButton.RegisterCallback(evt => { + if (IsRuntimeGraph){ + _runtimeGraph.ResolveDependency(); + AfterGraphResolved?.Invoke(); + } + }); + visualElement.Add(runButton); } public void RegisterDragEvent(){ @@ -341,8 +376,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 +425,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 +533,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 +551,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(); @@ -529,6 +573,8 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ }); menu.ShowAsContext(); }); + + } } @@ -557,12 +603,21 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ _data.NodeLinks.Remove(nodeLink); } - public bool TestMode{ get; set; } + public bool AutoUpdate{ + get=>Owner.graphEditorData.autoUpdate; set=>Owner.graphEditorData.autoUpdate = value; + } + + public override EventPropagation DeleteSelection(){ + Undo.RegisterCompleteObjectUndo(_data,"Delete Selection"); + var res = base.DeleteSelection(); + SaveGraphData(); + ResetGraphView(); + return res; + } public void CreateBlackboard(){ _blackboard = NodeEditorExtensions.CreateBlackboardWithGraphData(typeof(T)); _blackboard.Setup(this,Owner); - Debug.Log(Owner); var castedBlackboard = _blackboard as Blackboard; Add(castedBlackboard); Rect blackboardPos = new Rect(0,0,300,700); @@ -594,11 +649,9 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeGraphView{ public void NotifyRuntimeUpdate(){ - _runtimeGraphUpdate = true; } - - - public Action AfterRuntimeGraphUpdate{ get; set; } + + public Action AfterGraphResolved{ get; set; } #endregion } diff --git a/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs b/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs index c36a3b1..a94b9cf 100644 --- a/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs +++ b/TNode/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs @@ -18,6 +18,27 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ public abstract class BaseNodeView : Node,INodeView where T:NodeData,new(){ protected T _data; private readonly NodeInspectorInNode _nodeInspectorInNode; + private NodeViewLogger _viewLogger; + + private class NodeViewLogger:INodeLoggerImpl{ + public BaseNodeView NodeView { get; set; } + public void Log(string message){ + var loggerAreaParent = NodeView.extensionContainer; + if (loggerAreaParent == null){ + return; + } + var loggerArea = loggerAreaParent.Q("loggerArea"); + if(loggerArea == null){ + loggerArea = new TextField(); + loggerArea.name = "loggerArea"; + loggerArea.AddToClassList("loggerArea"); + loggerAreaParent.Add(loggerArea); + } + + loggerArea.multiline = true; + loggerArea.value = message; + } + } public IBaseDataGraphView BaseDataGraphView{ get{ @@ -31,18 +52,22 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ if(_data!=null) ((NodeDataWrapper)_data).OnValueChanged -= OnDataValueChanged; _data = value; + OnDataChanged?.Invoke(value); if(_data!=null) ((NodeDataWrapper)_data).OnValueChanged += OnDataValueChanged; - } } private void OnDataValueChanged(DataWrapper obj){ + Refresh(); + if (BaseDataGraphView == null) return; if (BaseDataGraphView.IsRuntimeGraph){ BaseDataGraphView.NotifyRuntimeUpdate(); } + + } public sealed override string title{ get => base.title; @@ -65,6 +90,13 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ if (_nodeInspectorInNode != null){ _nodeInspectorInNode.Data = obj; } + _viewLogger ??= new NodeViewLogger{NodeView = this}; + if (NodeLogger.Loggers.ContainsKey(obj.id)){ + NodeLogger.Loggers[obj.id] = _viewLogger; + } + else{ + NodeLogger.Loggers.Add(obj.id,_viewLogger); + } BuildInputAndOutputPort(); this.expanded = true; this.RefreshExpandedState(); @@ -106,7 +138,6 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ foreach (var propertyInfo in propertyInfos){ if (propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true).FirstOrDefault() is OutputAttribute attribute){ - Port port = new CustomPort(Orientation.Horizontal, Direction.Output, attribute.Multiple ? Port.Capacity.Multi : Port.Capacity.Single, BuildPortType(attribute, propertyInfo)); @@ -115,16 +146,18 @@ namespace TNode.TNodeGraphViewImpl.Editor.NodeViews{ var portName = ObjectNames.NicifyVariableName(BuildPortName(attribute,propertyInfo)); port.portName = portName; port.name = propertyInfo.Name; - } } 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; + } } } @@ -184,6 +217,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 +240,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