-
Notifications
You must be signed in to change notification settings - Fork 1
getting start
This lib is still under development!!
To create a graph ,use a built in graph creation window in t-Node ,click Asset/Create/TNode/Create New GraphEditor to create a new graph
it help you generate 3 files
let's go and have a look them
the first thing it generates is a graph data
using UnityEngine;
using System;
using TNodeCore.Runtime.Models;
[CreateAssetMenu(fileName = "New HelloGraph", menuName = "TNode/HelloGraph")]
[Serializable]
public class HelloGraph : GraphData{
}GraphData here is implemented as a scriptable object.so you can use the create asset menu to create a graph instance.
the second thing it generates is the editor script,so script gose like this
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using TNodeCore.Editor;
public class HelloEditor : GraphEditor<HelloGraph>{
[OnOpenAsset]
public static bool OnOpenAsset(int instanceID, int line){
var graph = EditorUtility.InstanceIDToObject(instanceID) as HelloGraph;
if (graph != null) {
var wnd = GetWindow<HelloEditor>();
wnd.titleContent = new GUIContent("HelloGraph Editor");
wnd.Show();
wnd.SetupNonRuntime(graph);
return true;
}
return false;
}
[MenuItem("Window/HelloEditor")]
public static void ShowWindow(){
var res = GetWindow<HelloEditor>();
res.titleContent = new GUIContent("HelloGraph Editor");
res.Show();
}
}it helps you set up basic editor settings .
The last thing is a graph editor data file.it stores some meta info for example which the implementation the current editor use,GraphView or GTF.
After create the definition of the graph and the editor, you can now create the graph and script a node.
The defualt creation path of the graph is already set by the creation tool if you use the method above.
But for a node you have to write it on your own right now . Create a new script named hello node for example
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using TNodeCore.Editor;
[GraphUsage(typeof(HelloGraph))]
public class HelloNode:NodeData{
[ShowInNode]
public string helloString = "Hello World!";
}A node must be specified with a [GraphUsage] attribute to register and be recognized as a valid node.
With this attribute provided, the default searching window of current graph could list it as a search result.
[ShowInNode] attribute make this field a property field inside the node so you can modifiy it.
If you want specify its searching type ,use
[GraphUsage(typeof(HelloGraph),"typeName")]There is one specified type of node could hold scene object information(runtime support required however).
[GraphUsage(typeof(HelloGraph))]
public class HelloSceneObjectNode:SceneObjectNode{
[ShowInNode]
public GameObject helloGo;
}Like other node tools (e.g xNode,NodeGraphProcessor) ,you define a port inside the definition of a node
The notable thing is tNode's port is a property rather than a field ,and latter the functional port would be support ,so the unity serialization system won't persist it.
let's go and see
[GraphUsage(typeof(HelloGraph))]
public class AddOperator:NodeData{
[Input]
public float A{get;set;}
[Input]
public float B{get;set;}
[Output]
public float Res{get;set;}
public override void Process(){
Res = A+B;
}
}The Input must contain a setter and output contain a getter , since you should not get input value outside the node and set the output value out the node ,the better definition goes.but the above usage is totally ok.
[GraphUsage(typeof(HelloGraph))]
public class AddOperator:NodeData{
[Input]
public float A{private get;set;}
[Input]
public float B{private get;set;}
[Output]
public float Res{get;private set;}
public override void Process(){
Res = A+B;
}
}To handle the input data and generate the output data.override process function like above.
public override void Process(){
Res = A+B;
}but you can also pre-process input value like this
[Input]
public float Reduce{
private get=>_reduce;
set=>_reduce+=value;
}
//you should not serialize this node data if you want to use it as a part of port
[NonSerialize]
private float _reduce;And since output is a property ,and if you don't need any further process info, you could directly handle output like this
[Output]public float Res=>A+B;By default tNode accept links with some rules.
When drag a edge from output port to input port. You need to make sure the type of output could be converted to type of the input
Note that an implicit conversion is automatically supported so a Vector3 port could connect to Vector2 and vice versa.
If no implicit conversion supported ,but you want make a direct edge,you could manually write a converter ,this happens in same cases you are not convenient to write implicit operator.
a converter goes like this.
[GraphUsage(typeof(HelloGraph))]
//this converter could convert a `camera` output port to `Vector3` input port
public class CameraToPosConverter:PortTypeConversion<Camera,Vector3>{
//Override this function to handle the conversion from T1(Camera) to T2(Vector3)
public override Vector3 Convert(Camera tFrom){
return tFrom.transform.position;
}
}PortTypeConversion is one way converter,if you want a two way converter,write like this
[GraphUsage(typeof(EasyGraph))]
public class CameraToGameObjectConverter:TwoWayPortTypeConversion<Camera,GameObject>{
public override GameObject Convert(Camera tFrom){
return tFrom.gameObject;
}
//Override this function to handle the conversion from T2(GameObject) to T1(Camera)
public override Camera ConvertBack(GameObject tTo){
return tTo.GetComponent<Camera>();
}
}Note there is also a [GraphUsage(typeof(EasyGraph))] attribute used on the converter.But right now this attribute only works as the register symbol for the cahce,means that converters works globally.
A blackboard holds data as exposed parameter.Every field defined in blackboard will be considered as exposed parameter unless the type is not supported.
Currently ,collections that are not list or array will be ignored.
A blackboard data stricts type of exposed parameters.so a graph only have acess to specified type and numbers of parameters.
A script of blackboard data goes like this
[GraphUsage(typeof(HelloGraph))]
public class HelloBlackboard:BlackboardData{
public string HelloString;
public GameObject HelloGameObject;
public List<Vector3> V3Collection;
public List<Vector2> V2Collection;
}Unlike a scene object node that you have manually set the output port to output the data, Drag an entry in blackboard to the editor ,it will automatically create a node outputs the selected entry data .
Note although the field types are defined ,the field value could differ from two runtime graphs even the two graph share the same static graph.this runtime difference also applied to scene object node
The scene node share the same purpose of blackboard . But instead of holding data in a runtime graph component.A scene object node will store its persistent information on a automatically created game object.and you have to manually set the outport to output the data.
Runtime graph is a component hold a reference to a graph ,and every runtime graph holds independent copies of blackboard data and scene object node
A runtime graph support test,depedency traversal,and so on.
To use a runtime graph ,create a gameobject and add the RuntimeGraph Component.You don't need to implement or extend the class on your own by default.
Currently there are two types of traversal both only work on a non-circular graph.
The circular graph for a purpose of something like FSM is not implemented yet and only a static graph could hold that.but A runtime version is on its way.
To use a runtime graph,make sure your graph is non-circular and then,in a place you could access to the runtime graph.
public RuntimeGraph _graph;
public void Start(){
//Run hello nodes , every node will automatically resolve its dependency and run.the nodes in the dependency path will also be processed to generate outputs
_graph.RunNodesOfType<HelloNode>();
}
Since every node in a run would resolve its dependency on its own.It will be expensive to run in a large scale graph. So you can set the runtime graph to cache mode to speed up the run ,in this mode,output data will only acquired once and cached in runtime graph.the next time the runtime node access to the output data in this run,it will fetch the cache instead. Note the cache don't share between two run right now.
Make sure you use this feature in a graph that output data of a node won't be changed during a run.
public RuntimeGraph _graph;
public void Start(){
//To enable a cached run ,set this variable to true.
_graph.RunNodesOfType<HelloNode>(true);
}Nodes run at default order.if you want a specified running order. fetch the nodes and sort it.
public RuntimeGraph _graph;
public void Start(){
nodes = _graph.GetRuntimeNodesOfType<HelloNode>();
nodes.Sort(/* Some sort method here */)
//The RunNodes method accept a list of RuntimeNode,however,these runtime node must be already contained in the graph
_graph.RunNodes(nodes);
}And further more,the runtime graph have an already topologically sorted cache.if you have no special need.you can traverse all nodes in a topological order.
public RuntimeGraph _graph;
public void Start(){
_graph.TraverseAll();
}Runtime Graph could log info to it's node.the log info will be showed below the inspector insde the node.