Getting Started - McManning/BlueGraph GitHub Wiki
You have a ScriptableObject that inherits from BlueGraph.Graph
and contains a collection of different node instances inheriting from BlueGraph.Node
.
Each node contains input and output BlueGraph.Port
instances that are connected by edges to the ports of other nodes on the same graph.
Nodes are essentially units of work in a graph. For example, a graph that generates a procedural Terrain heightmap may have nodes for creating fBM or Perlin noise, nodes for blending multiple noises together, nodes for adding trees and other assets to the Terrain, etc.
Another example may be an NPC dialog system where some nodes represent blocks of conversation text, and other nodes represent the conditions that can trigger this text to be displayed.
A graph is saved to a single asset file in your project. This contains all the information about each node's stored field values and connection states to other nodes.
For most use cases, nodes and edges are typically modified from within the Unity Editor and the structure of the graph is treated as read-only during runtime.
Editing a graph is done within a CanvasView
contained inside a GraphEditorWindow
.
Each node on the graph has an associated NodeView
, either the standard view or a custom view defined by an attribute on the node's class. The NodeView is responsible for rendering a child PortView
element per port and any additional editable controls.
Edges connecting nodes are colored to represent the type of data being passed through that edge (e.g. a connection between two float ports will be a different color than the connection between two string ports).
Within the editor, you may only connect two ports that are data-compatible. For example, an int output could connect to a float input because these can be safely cast. But you could not connect a string output to a float input without adding a custom node to safely handle that conversion for you.
All graph assets are ScriptableObjects inherited from BlueGraph.Graph
and have a way to be instantiated from within Unity - e.g. through [CreateAssetMenu]
.
using BlueGraph;
[CreateAssetMenu(
menuName = "Example Graph",
fileName = "New Example Graph"
)]
public class ExampleGraph : Graph
{
}
At a bare minimum you do not need to add anything to the body of the graph class. But based on your use case, you'll want to add methods for executing or retrieving output values from nodes.
For example, let's say you have a graph that procedurally generates a Texture2D through a series of nodes. At the end of your graph (that all other nodes eventually feed into as inputs) you have an OutputTextureNode
. You might then want to add a method like the following:
[CreateAssetMenu(
menuName = "Procedural Texture",
fileName = "New Procedural Texture"
)]
public class ProceduralTextureGraph : Graph
{
public Texture2D Generate()
{
var outputNode = GetNode<OutputTextureNode>();
var result = outputNode.GetOutputValue<Texture2D>("Result");
return result;
}
}
As part of your MonoBehaviour or other system, you can then reference a copy of the graph as a typical ScriptableObject and execute your custom method:
public class ApplyProceduralTexture : MonoBehaviour
{
public ProceduralTextureGraph proceduralTextureGraph;
void Start()
{
Texture2D texture = proceduralTextureGraph.Generate();
GetComponent<Renderer>().material.mainTexture = texture;
}
}
Some optional class attributes are available to Graphs. Check out Graph Attributes for more information.
All nodes inherit from BlueGraph.Node
and have a [Node]
attribute on the class:
using BlueGraph;
[Node(path = "Common/Math")]
public class MultiplyFloats : Node
{
[Input] public float a;
[Input] public float b;
[Output] public float result;
public override object OnRequestValue(Port port)
{
float inA = GetInputValue("a", a);
float inB = GetInputValue("b", b);
return inA * inB;
}
}
The [Node]
attribute helps the editor find and automatically setup nodes for a Graph. You can control how the node is organized in the search window by specifying a slash-delimited path
argument.
When decorating a field with an [Input]
or [Output]
attribute, a BlueGraph.Port
is added with the same name and type. These ports can accept compatible connections from the ports of other nodes on your graph.
Any public fields decorated with the [Input]
attribute will have their values directly editable from within the canvas. These values can be used as fallback constants for when no connections are made to their associated input port.
For more information on all the attributes available to nodes check out Node Attributes.
Nodes that are connected to an output port will execute that node's OnRequestValue
with port as an argument. This method must be implemented on every node and contains the bulk of your node's logic. The return value from OnRequestValue
will be passed to the inputs of other nodes and used by those nodes for their own OnRequestValue
implementations.
If your node has multiple output ports, you will want to conditionally run logic based on which port is being requested:
public override void OnRequestValue(Port port)
{
// Each port is named the same as the [Output] field
switch (port.Name) {
case "a":
// Logic for output port a
case "b":
// Logic for output port b
}
}
Note that if an output port is connected to multiple inputs the
OnRequestValue
method may be called for each input that wishes to read the value at the connection. BlueGraph does not perform of any sort of memoization of output values.
Use GetInputValue()
to retrieve the value of one of your input ports from its connection. The OnRequestValue
method of the connected node will be called, and the return value passed back. If there is no connection to that input it will fallback to the value in the second argument - which is typically the associated field that we expose in the editor.
For information on methods available, see Node Methods.
Once you have a Graph and a set of nodes to work with you can create an instance of your Graph and start working in the editor.
See Working With the Canvas for the basics.